diff --git a/.github/workflows/compas-compile-ci.yml b/.github/workflows/compas-compile-ci.yml index 66ca2296c..4ee1eb40d 100644 --- a/.github/workflows/compas-compile-ci.yml +++ b/.github/workflows/compas-compile-ci.yml @@ -1,6 +1,7 @@ name: COMPAS compile test -'on': - workflow_dispatch: null + +on: + workflow_dispatch: pull_request: branches: - dev @@ -12,7 +13,7 @@ name: COMPAS compile test push: branches: - dev - - + # Ensures only the latest workflow run for the same branch is active, canceling any in-progress runs. concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -25,53 +26,63 @@ jobs: ARTIFACT_PATH: py_tests/test_artifacts name: Build COMPAS - runs-on: '${{ matrix.os}}' - container: '${{matrix.container}}' - strategy: - fail-fast: false - matrix: - os: - - ubuntu-22.04 + runs-on: ubuntu-22.04 + container: teamcompas/compas:latest + # TODO: Switch to GHCR when package is made public + # container: ghcr.io/teamcompas/compas:latest + steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.9' - cache: pip - cache-dependency-path: setup.py - # - name: Install TeXLive - # uses: teatimeguest/setup-texlive-action@v3.3.4 - - name: Install dependencies on ubuntu - if: 'startsWith(matrix.os, ''ubuntu-2'')' - run: | - cd misc/cicd-scripts - chmod 755 linux-dependencies - ./linux-dependencies - - name: Build Compas + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build COMPAS from source run: | - cd src && make -j $(nproc) -f Makefile + echo "Building COMPAS from source..." + cd src + make clean 2>/dev/null || echo 'No existing build to clean' + make -j $(nproc) -f Makefile ./COMPAS -v - - name: Install python utils + echo "COMPAS build completed successfully!" + + - name: Install Python dependencies run: | - pip install --upgrade pip + echo "Installing Python dependencies..." + python3 -m pip install --upgrade pip pip install -e .[dev] + echo "Python dependencies installed!" + - name: Run example COMPAS job run: | + echo "Running example COMPAS job..." export COMPAS_EXECUTABLE_PATH=${GITHUB_WORKSPACE}/src/COMPAS cd ${GITHUB_WORKSPACE}/py_tests/test_data/ chmod 755 run.sh + echo "Contents of run.sh:" cat run.sh ./run.sh - - name: Run pytests + echo "Example job completed!" + + - name: Run pytest suite run: | + echo "Running pytest suite..." cd ${GITHUB_WORKSPACE} export COMPAS_ROOT_DIR=${GITHUB_WORKSPACE} - jupyter-kernelspec list + + if command -v jupyter-kernelspec &> /dev/null; then + echo "Available Jupyter kernels:" + jupyter-kernelspec list + fi + pytest --cov=compas_python_utils/ py_tests/ -m 'not webtest' pytest --cov=compas_python_utils/ --cov-append py_tests/ -m webtest - ls py_tests/test_artifacts + + echo "Test artifacts:" + ls py_tests/test_artifacts/ 2>/dev/null || echo "No test artifacts found" + coverage html coverage-badge -o coverage_badge.svg -f + echo "All tests completed successfully!" + - name: Archive code coverage results uses: actions/upload-artifact@v4 with: @@ -79,17 +90,67 @@ jobs: path: | htmlcov/ coverage_badge.svg + - name: Archive COMPAS detailed-evolution plot id: upload uses: actions/upload-artifact@v4 with: - name: '${{ env.ARTIFACT_NAME }}' - path: '${{ env.ARTIFACT_PATH }}/${{ env.ARTIFACT_NAME }}' + name: evolution-plot-${{ github.run_number }} + path: ${{ env.ARTIFACT_PATH }}/${{ env.ARTIFACT_NAME }} if-no-files-found: error + - name: Test Summary run: | - echo "### Test Results" >> $GITHUB_STEP_SUMMARY - echo "- Compas Build: Success" >> $GITHUB_STEP_SUMMARY - echo "- Python Utils Installation: Success" >> $GITHUB_STEP_SUMMARY + echo "### COMPAS CI Results" >> $GITHUB_STEP_SUMMARY + echo "- COMPAS Build: Success" >> $GITHUB_STEP_SUMMARY + echo "- Python Dependencies: Success" >> $GITHUB_STEP_SUMMARY echo "- Example COMPAS Job: Success" >> $GITHUB_STEP_SUMMARY - echo "- Pytest Execution: Success" >> $GITHUB_STEP_SUMMARY + echo "- Pytest Suite: Success" >> $GITHUB_STEP_SUMMARY + + comment-with-plot: + name: Comment PR with Evolution Plot + runs-on: ubuntu-22.04 + container: docker://ghcr.io/iterative/cml:0-dvc2-base1 + needs: compas + if: github.event_name == 'pull_request' + + env: + ARTIFACT_NAME: detailedEvolutionPlot.png + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download evolution plot + uses: actions/download-artifact@v4 + with: + name: evolution-plot-${{ github.run_number }} + + - name: Create report with evolution plot + env: + REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "## ✅ COMPAS Build Successful!" >> report.md + echo "" >> report.md + echo "| Item | Value |" >> report.md + echo "|------|-------|" >> report.md + echo "| **Commit** | [\`$(echo $GITHUB_SHA | cut -c1-7)\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}) |" >> report.md + echo "| **Logs** | [View workflow](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}) |" >> report.md + echo "" >> report.md + + if [ -f "${{ env.ARTIFACT_NAME }}" ]; then + echo "### Detailed Evolution Plot" >> report.md + echo "
Click to view evolution plot" >> report.md + echo "" >> report.md + echo "![](./${{ env.ARTIFACT_NAME }})" >> report.md + echo "
" >> report.md + else + echo "### ⚠️ Evolution plot not found" >> report.md + fi + + echo "" >> report.md + echo "---" >> report.md + echo "Generated by COMPAS CI " >> report.md + + # Post the report using CML + cml comment create report.md \ No newline at end of file diff --git a/.github/workflows/dockerhub-ci.yml b/.github/workflows/dockerhub-ci.yml index f032aa9a7..e441367fc 100755 --- a/.github/workflows/dockerhub-ci.yml +++ b/.github/workflows/dockerhub-ci.yml @@ -5,6 +5,11 @@ on: branches: [ dev ] workflow_dispatch: null +# Add permissions for the built-in GITHUB_TOKEN +permissions: + contents: read + packages: write + # Ensures only the latest workflow run for the same branch is active, canceling any in-progress runs. concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -23,11 +28,23 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Get release version run: echo "COMPAS_VERSION=$(sed -n '/const std::string VERSION_STRING/,/^$/p' ./src/changelog.h | sed 's/.*"\(.*\)"[^"]*$/\1/')" >> $GITHUB_ENV - - name: Print version - run: echo $COMPAS_VERSION + - name: Set lowercase repository name + run: echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV + + - name: Print version and repo + run: | + echo "Version: $COMPAS_VERSION" + echo "Lowercase repo: $REPO_LC" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -40,6 +57,7 @@ jobs: tags: | teamcompas/compas:${{ env.COMPAS_VERSION }} teamcompas/compas:latest + ghcr.io/${{ env.REPO_LC }}:${{ env.COMPAS_VERSION }} + ghcr.io/${{ env.REPO_LC }}:latest cache-from: type=gha - cache-to: type=gha,mode=max - + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.github/workflows/pr_artifact_url_commenter.yml b/.github/workflows/pr_artifact_url_commenter.yml deleted file mode 100644 index 62bca978d..000000000 --- a/.github/workflows/pr_artifact_url_commenter.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Comment on Pull request - -on: - workflow_run: - workflows: ['COMPAS compile test'] - types: - - completed - -jobs: - comment: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - name: Get Artifact and Pull request info - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WORKFLOW_RUN_EVENT_OBJ: ${{ toJSON(github.event.workflow_run) }} - OWNER: ${{ github.repository_owner }} - REPO: ${{ github.event.repository.name }} - run: | - PREVIOUS_JOB_ID=$(jq -r '.id' <<< "$WORKFLOW_RUN_EVENT_OBJ") - echo "PREVIOUS_JOB_ID=$PREVIOUS_JOB_ID" >> "$GITHUB_ENV" - - SUITE_ID=$(jq -r '.check_suite_id' <<< "$WORKFLOW_RUN_EVENT_OBJ") - echo "SUITE_ID=$SUITE_ID" >> "$GITHUB_ENV" - - ARTIFACT_ID=$(gh api "/repos/$OWNER/$REPO/actions/artifacts" \ - --jq ".artifacts[] | select(.workflow_run.id==$PREVIOUS_JOB_ID and .expired==false and .name=='detailedEvolutionPlot.png') | .id" | head -n1) - echo "ARTIFACT_ID=$ARTIFACT_ID" >> "$GITHUB_ENV" - - PR_NUMBER=$(gh api "/repos/$OWNER/$REPO/commits/${{ github.event.workflow_run.head_sha }}/pulls" --jq '.[0].number') - echo "PR_NUMBER=${PR_NUMBER:-null}" >> "$GITHUB_ENV" - - HEAD_SHA=${{ github.event.workflow_run.head_sha }} - echo "HEAD_SHA=$HEAD_SHA" >> "$GITHUB_ENV" - - - name: Download artifact - if: env.PR_NUMBER != 'null' && env.ARTIFACT_ID != '' - run: | - gh api /repos/${{ github.repository }}/actions/artifacts/${{ env.ARTIFACT_ID }}/zip -H "Accept: application/vnd.github.v3+json" --output artifact.zip - unzip artifact.zip - rm artifact.zip - - - name: Update Comment - if: env.PR_NUMBER != 'null' && env.ARTIFACT_ID != '' - env: - JOB_PATH: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ env.PREVIOUS_JOB_ID }}" - ARTIFACT_URL: "${{ github.server_url }}/${{ github.repository }}/suites/${{ env.SUITE_ID }}/artifacts/${{ env.ARTIFACT_ID }}" - uses: actions/github-script@v6 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const fs = require('fs'); - const imageData = fs.readFileSync('detailedEvolutionPlot.png', {encoding: 'base64'}); - github.rest.issues.createComment({ - issue_number: process.env.PR_NUMBER, - owner: context.repo.owner, - repo: context.repo.name, - body: ` - ![badge] - - Build Successful! You can find a link to the downloadable artifact below. - - | Name | Link | - | -------- | ----------------------- | - | Commit | ${process.env.HEAD_SHA} | - | Logs | ${process.env.JOB_PATH} | - | Download | ${process.env.ARTIFACT_URL} | - - ### Detailed Evolution Plot - ![Detailed Evolution Plot](data:image/png;base64,${imageData}) - - [badge]: https://img.shields.io/badge/Build_Success!-0d1117?style=for-the-badge&labelColor=3fb950&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmZmZmZmIj48cGF0aCBkPSJNMjEuMDMgNS43MmEuNzUuNzUgMCAwIDEgMCAxLjA2bC0xMS41IDExLjVhLjc0Ny43NDcgMCAwIDEtMS4wNzItLjAxMmwtNS41LTUuNzVhLjc1Ljc1IDAgMSAxIDEuMDg0LTEuMDM2bDQuOTcgNS4xOTVMMTkuOTcgNS43MmEuNzUuNzUgMCAwIDEgMS4wNiAwWiI+PC9wYXRoPjwvc3ZnPg== - ` - }) diff --git a/Dockerfile b/Dockerfile index a6a335a2f..2eb174d8f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,10 @@ -FROM ubuntu:18.04 +FROM ubuntu:22.04 WORKDIR /app/COMPAS +# Prevent interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive + RUN apt-get update && apt-get install -y \ g++ \ libhdf5-serial-dev \ @@ -9,11 +12,11 @@ RUN apt-get update && apt-get install -y \ libgsl-dev \ python3 \ python3-pip \ - zip + zip \ + && rm -rf /var/lib/apt/lists/* -# RUN pip install numpy awscli -RUN pip3 install numpy -RUN pip3 install pyyaml +# Install Python packages +RUN pip3 install numpy pyyaml # Copy only the source required to compile COMPAS COPY src/ src/ @@ -23,7 +26,7 @@ RUN mkdir obj bin logs ENV COMPAS_ROOT_DIR /app/COMPAS # Compile COMPAS -RUN cd src && make -f Makefile.docker -j $(nproc) +RUN cd src && make DOCKER_BUILD=1 -j $(nproc) # Run COMPAS -# CMD [ "python", "src/pythonSubmitDefault.py" ] +# CMD [ "python", "src/pythonSubmitDefault.py" ] \ No newline at end of file diff --git a/README.md b/README.md index adc6e7d37..11ff49b4f 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,19 @@ https://compas.science/docs Please email your queries to compas-user@googlegroups.com. You are also welcome to join the [COMPAS User Google Group](https://groups.google.com/forum/#!members/compas-user) to engage in discussions with COMPAS users and developers. ## Acknowledgements -If you use this code or parts of this code for results presented in a scientific publication, we would greatly appreciate if you send us your paper reference and make your input settings and output data publicly available by uploading it to the [COMPAS Zenodo community](https://zenodo.org/communities/compas/). Please also kindly include citations to our COMPAS methods paper https://ui.adsabs.harvard.edu/abs/2021arXiv210910352T/abstract. As the public COMPAS code is a product of work by the entire COMPAS collaboration over many years, we kindly request that, in recognition of this team effort, the paper is cited as “Team COMPAS: J. Riley et al.”. An example bibtex code is: - -@ARTICLE{2022ApJS..258...34R, - author = {{Riley}, Jeff and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold and {Team Compas}}, +If you use this code or parts of this code for results presented in a scientific publication, we would greatly appreciate if you send us your paper reference and make your input settings and output data +publicly available by uploading it to the [COMPAS Zenodo community](https://zenodo.org/communities/compas/). Please also kindly include citations to our COMPAS methods papers +https://ui.adsabs.harvard.edu/abs/2022ApJS..258...34R +and +https://ui.adsabs.harvard.edu/abs/2025ApJS..280...43T . +As the public COMPAS code is a product of work by the entire COMPAS collaboration over many years, we kindly request that, in recognition of +this team effort, the papers are cited as “Team COMPAS: J. Riley et al. (2021)” and "Team COMPAS: I. Mandel et al. (2025). Example bibtex codes are: + +@ARTICLE{compas2021, + author = {{Team COMPAS: Riley}, Jeff and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold}, title = "{Rapid Stellar and Binary Population Synthesis with COMPAS}", journal = {\apjs}, - keywords = {1622, 154, 1108, 162, Astrophysics - Instrumentation and Methods for Astrophysics, Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Solar and Stellar Astrophysics}, + keywords = {1622, 154, 1108, 162}, year = 2022, month = feb, volume = {258}, @@ -30,20 +36,35 @@ If you use this code or parts of this code for results presented in a scientific eid = {34}, pages = {34}, doi = {10.3847/1538-4365/ac416c}, -archivePrefix = {arXiv}, - eprint = {2109.10352}, - primaryClass = {astro-ph.IM}, adsurl = {https://ui.adsabs.harvard.edu/abs/2022ApJS..258...34R}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } +@ARTICLE{compas2025, + author = {{Team COMPAS: Mandel}, Ilya and {Riley}, Jeff and {Boesky}, Adam and {Brcek}, Adam and {Hirai}, Ryosuke and {Kapil}, Veome and {Lau}, Mike Y.~M. and {Merritt}, JD and {Rodr{\'\i}guez-Segovia}, Nicol{\'a}s and {Romero-Shaw}, Isobel and {Song}, Yuzhe and {Stevenson}, Simon and {Vajpeyi}, Avi and {van Son}, L.~A.~C. and {Vigna-G{\'o}mez}, Alejandro and {Willcox}, Reinhold}, + title = "{Rapid Stellar and Binary Population Synthesis with COMPAS: Methods Paper II}", + journal = {\apjs}, + keywords = {Binary stars, Stellar populations, Stellar evolution, Stellar evolutionary models, Stellar remnants, 154, 1622, 1599, 2046, 1627, Solar and Stellar Astrophysics, High Energy Astrophysical Phenomena, Instrumentation and Methods for Astrophysics}, + year = 2025, + month = sep, + volume = {280}, + number = {1}, + eid = {43}, + pages = {43}, + doi = {10.3847/1538-4365/adf8d0}, +archivePrefix = {arXiv}, + eprint = {2506.02316}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2025ApJS..280...43T}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} -Note that the preferred acknowledgement relies on \noopsort and cites the paper as TEAM COMPAS; Riley et al.; to make it work, you'll have to include the following line at the start of your bibtex file: -@PREAMBLE{ {\providecommand{\noopsort}[1]{}} } +##Note that the preferred acknowledgement relies on \noopsort and cites the paper as TEAM COMPAS; Riley et al.; to make it work, you'll have to include the following line at the start of your bibtex file: +##@PREAMBLE{ {\providecommand{\noopsort}[1]{}} } -and change the author line in the bib entry to: +##and change the author line in the bib entry to: -author = {{\noopsort{Team COMPAS}}{Team COMPAS: Riley, J.} and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-Gomez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold} +##author = {{\noopsort{Team COMPAS}}{Team COMPAS: Riley, J.} and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-Gomez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold} diff --git a/compas_matlab_utils/BSEDetailedOutput.m b/compas_matlab_utils/BSEDetailedOutput.m new file mode 100644 index 000000000..ae57fb826 --- /dev/null +++ b/compas_matlab_utils/BSEDetailedOutput.m @@ -0,0 +1,79 @@ +function BSEDetailedOutput(file) +% Carries out some basic analysis and makes plots for a single BSE run +% +% USAGE: +% BSEDetailedOutput(file) +% +% INPUTS: +% file: name of detailed output file in COMPAS h5 format +% +% examples: +% BSEDetailedOutput('~/Work/COMPAS/src/COMPAS_Output/Detailed_Output/BSE_Detailed_Output_0.h5') +% + +time=h5read(file,'/Time'); +MThistory=h5read(file,'/MT_History'); +Z=h5read(file,'/Metallicity@ZAMS(1)'); +mass1=h5read(file,'/Mass(1)'); +massCO1=h5read(file,'/Mass_CO_Core(1)'); +mass2=h5read(file,'/Mass(2)'); +massCO2=h5read(file,'/Mass_CO_Core(2)'); +massHe1=h5read(file,'/Mass_He_Core(1)'); +massHe2=h5read(file,'/Mass_He_Core(2)'); +luminosity1=h5read(file,'/Luminosity(1)'); +luminosity2=h5read(file,'/Luminosity(2)'); +type1=h5read(file,'/Stellar_Type(1)'); +type2=h5read(file,'/Stellar_Type(2)'); +radius1=h5read(file,'/Radius(1)'); +radius2=h5read(file,'/Radius(2)'); +RL1=h5read(file,'/RocheLobe(1)'); +RL2=h5read(file,'/RocheLobe(2)'); +a=h5read(file,'/SemiMajorAxis'); +e=h5read(file,'/Eccentricity'); +rec=h5read(file,'/Record_Type'); +AM1=h5read(file, '/Ang_Momentum(1)'); +AM2=h5read(file, '/Ang_Momentum(2)'); +AMtot=h5read(file, '/Ang_Momentum_Total'); +omega1=h5read(file, '/Omega(1)'); +omega2=h5read(file, '/Omega(2)'); +omegab1=h5read(file, '/Omega_Break(1)'); +omegab2=h5read(file, '/Omega_Break(2)'); +MTtimescale=h5read(file, '/MassTransferTimescale'); + + +disp('Time (Myr), Event, M1 (M_o), type1, M2 (M_o), type2, a (R_o), e'); +disp([num2str(time(1)), ' start: Z=', num2str(Z(1)), ' ', num2str(mass1(1)), ' ', num2str(type1(1)),... + ' ', num2str(mass2(1)), ' ', num2str(type2(1)), ' ', num2str(a(1)), ' ', num2str(e(1))]); +for(i=2:length(time)), + if(rec(i)==4), %only print records at end of timestep + prev=find(rec(1:i-1)==4, 1, 'last'); %previous timestep index + if(MThistory(i)~=0),% & MThistory(i)~=MThistory(i-1)), + switch (MThistory(i)), case 1, MTstring='Stable MT from 1 TO 2'; case 2, MTstring='Stable MT from 2 TO 1';... + case 3, MTstring='CE from 1 to 2'; case 4, MTstring='CE from 2 to 1'; case 5, MTstring='CE Double Core'; ... + case 6, MTstring='CE merger'; end; + switch(MTtimescale(i)), case 1, MTtimescalestring='Nuclear'; case 2, MTtimescalestring='Thermal'; case 3, ... + MTtimescalestring='Dynamical'; end; + disp([num2str(time(i)), ' ', MTstring, ', @', MTtimescalestring, ' ', num2str(mass1(i)), ' ', num2str(type1(i)),... + ' ', num2str(mass2(i)), ' ', num2str(type2(i)), ' ', num2str(a(i)), ' ', num2str(e(i))]); + end; + if(type1(i)~=type1(prev)) + disp([num2str(time(i)), ' star 1 ', num2str(type1(prev)), '->', num2str(type1(i)), ' ', num2str(mass1(i)), ' ', num2str(type1(i)),... + ' ', num2str(mass2(i)), ' ', num2str(type2(i)), ' ', num2str(a(i)), ' ', num2str(e(i))]); + end; + if(type2(i)~=type2(prev)), + disp([num2str(time(i)), ' star 2 ', num2str(type2(prev)), '->', num2str(type2(i)), ' ', num2str(mass1(i)), ' ', num2str(type1(i)),... + ' ', num2str(mass2(i)), ' ', num2str(type2(i)), ' ', num2str(a(i)), ' ', num2str(e(i))]); + end; + end; +end; +if((type1(i)==13 || type1(i)==14) && (type2(i)==13 || type2(i)==14) ), + Msunkg=1.98892e30; c=299792458; G=6.67428e-11; Rsun = 695500000; + beta=64/5*G^3*mass1(i)*mass2(i)*(mass1(i)+mass2(i))*Msunkg^3/c^5; T0=(a(i)*Rsun)^4/4/beta; + Tdelay=T0*(1-e(i)^2).^(7/2).*(1+0.27*e(i)^10 + 0.33*e(i)^20 + 0.2*e(i)^1000)/3.15e7/1e6; + disp([num2str(time(i)+Tdelay), ' GW merger in ', num2str(Tdelay,'%.0f'), ' Myr ', num2str(mass1(i)), ' ', num2str(type1(i)),... + ' ', num2str(mass2(i)), ' ', num2str(type2(i))]); +end; + +figure(1), plot(time, mass1, 'LineWidth', 3), set(gca,'FontSize',20), xlabel('Time, Myr'), ylabel('Total mass 1, M_o') +%figure(2), scatter(time, radius1, 30, type1, 'filled'), set(gca,'FontSize',20), xlabel('Time, Myr'), ylabel('Radius 1, R_o') +figure(2), plot(time, radius2, 'b', time, RL2, 'r'); set(gca,'FontSize',20), xlabel('Time, Myr'), ylabel('Radius, R_o'), legend('R_2', 'RL_2') diff --git a/compas_matlab_utils/ComparisonPlots.m b/compas_matlab_utils/ComparisonPlots.m new file mode 100644 index 000000000..69833d36f --- /dev/null +++ b/compas_matlab_utils/ComparisonPlots.m @@ -0,0 +1,421 @@ +function ComparisonPlots(filename1, name1, filename2, name2) +% Carries out some basic analysis and makes plots comparing two COMPAS runs +% +% USAGE: +% ComparisonPlots(filename1, name1, filename2, name2) +% +% INPUTS: +% filename1: name of population synthesis input file 1 in COMPAS h5 format +% name1: name of data set 1 for plot legends +% filename2: name of population synthesis input file 2 in COMPAS h5 format +% name2: name of data set 2 for plot legends +% +% the last two arguments are optional; as single output can be plotted +% by running ComparisonPlots(filename1, name1) +% +% example: +% ComparisonPlots('~/Work/COMPASresults/runs/Zsolaralpha1-031803.h5', 'Default', ... +% '~/Work/COMPASresults/runs/Zsolar2stage-031803.h5', '2 stage') +% +% Warning: --switch-log must be used for all runs to be analysed +% It is recommended, but not required, to use the same random seed for the +% runs being compared in order to compare individual binary evolution + + + global Msunkg G AU Rsun + Msunkg=1.98892e30; %Msun in kg + G=6.67428e-11; %G in m^3/kg/s^2 + AU=149597871e3; %m + Rsun = 695500000; %m + + %Plot DCO mass distribution, BNS P-e distribution, chirp mass vs period + %at DCO formation, BH mass vs secondary core mass for 2->1 CEs leading + %to merging BBH formation + figure(1); clf(1); figure(2); clf(2); figure(3); clf(3); figure(4); clf(4); figure(5); clf(5); + [BNS,NSBH,BBH,BNSCE,NSBHCE,BBHCE,CEBBH1]=DCOplot(filename1, name1, 1, 'r', 40); + fprintf('\nDCOs:\t\t#Merging DNS\t#Merging NSBH\t#Merging BBH\t%% BNS via CE\t%% NSBH via CE\t%% BBH via CE\t%% BBH via CE with BH primary\n'); + fprintf('%s:\t%d\t\t%d\t\t%d\t\t%.0f\t\t%.0f\t\t%.0f\t\t%.0f\n', ... + name1, BNS, NSBH, BBH, BNSCE/BNS*100, NSBHCE/NSBH*100, BBHCE/BBH*100, CEBBH1/BBH*100); + if(nargin==4), + [BNS,NSBH,BBH,BNSCE,NSBHCE,BBHCE,CEBBH1]=DCOplot(filename2, name2, 1, 'b', 20); + fprintf('%s:\t%d\t\t%d\t\t%d\t\t%.0f\t\t%.0f\t\t%.0f\t\t%.0f\n', ... + name2, BNS, NSBH, BBH, BNSCE/BNS*100, NSBHCE/NSBH*100, BBHCE/BBH*100, CEBBH1/BBH*100); + end; + figure(1), hold off; figure(2), hold off; figure(3), hold off; figure(4), hold off; + figure(1), set(gca,'FontSize',20), xlabel('$M_1$ (M$_\odot$)', 'Interpreter', 'latex'), + ylabel('$M_2$ (M$_\odot$)', 'Interpreter', 'latex'), title('Merging DCO masses'); legend; + figure(2), set(gca,'FontSize',20), xlabel('$\log_{10}$ (Orbital period/hr)', 'Interpreter', 'latex'); + ylabel('Eccentricity'), title('DNS at formation'); legend; + figure(3), set(gca,'FontSize',20), xlabel('Chirp Mass (M$_\odot$)', 'Interpreter', 'latex'), + ylabel('$\log_{10} (P_\mathrm{orb}/\mathrm{d})$', 'Interpreter', 'latex'), title('Merging BBH at formation'); legend; + figure(4), set(gca,'FontSize',20); xlabel('$M_1$ (M$_\odot$)', 'Interpreter', 'latex'), + ylabel('$M_{\textrm{core},2}$ (M$_\odot$)', 'Interpreter', 'latex'), title('CE from 2->1 en route to merging BBH'), legend; + figure(5), set(gca,'FontSize',20); xlabel('$q \equiv M_2/M_1$', 'Interpreter', 'latex'), + ylabel('CDF'), title('CDF of merging BBH'), legend; + fignumber=6; + + %Plot BH HMXBs + figure(fignumber); clf(fignumber); + HMXBplot(filename1, name1, fignumber, 'r', 40); + if(nargin==4), + HMXBplot(filename2, name2, fignumber, 'b', 20); + end; + figure(fignumber); hold off; set(gca,'FontSize',20), legend; title('HMXB masses'); + xlabel('BH mass (M$_\odot$)', 'Interpreter', 'latex'), ylabel('Companion mass (M$_\odot$)', 'Interpreter', 'latex'); + fignumber=fignumber+1; + + %Plot BeXRBs + figure(fignumber); clf(fignumber); + BeXRBplot(filename1, name1, fignumber, 'r', 40); + if(nargin==4), + BeXRBplot(filename2, name2, fignumber, 'b', 20); + end; + figure(fignumber); hold off; set(gca,'FontSize',20), legend; + xlabel('Companion mass (M$_\odot$)', 'Interpreter', 'latex'); + ylabel('Formation time, Myr'), title('BeXRBs just after SN'); + fignumber=fignumber+1; + + %Plot LMXBs/IMXBs + figure(fignumber); clf(fignumber); + [LMXBcount, NSLMXBcount]=LMXBplot(filename1, name1, fignumber, 'r', 40); + fprintf('\nLMXBs:\t\t#LMXB\t\t#NS LMXB\n'); + fprintf('%s:\t%d\t\t%d\n', name1, LMXBcount, NSLMXBcount); + if(nargin==4), + [LMXBcount, NSLMXBcount]=LMXBplot(filename2, name2, fignumber, 'b', 20); + fprintf('%s:\t%d\t\t%d\n', name2, LMXBcount, NSLMXBcount); + end; + figure(fignumber), hold off; set(gca,'FontSize', 20); legend; title('LMXB on first MT onto CO'); + xlabel('Compact object mass (M$_\odot$)', 'Interpreter', 'latex'), ylabel('Companion mass (M$_\odot$)', 'Interpreter', 'latex'); + fignumber=fignumber+1; + + %Plot DWDs (just as a sanity check) + figure(fignumber); clf(fignumber); + DWDplot(filename1, name1, fignumber, 'r', 'm', 10); + if(nargin==4), + DWDplot(filename2, name2, fignumber, 'b', 'g', 5); + end; + figure(fignumber); hold off; axis([-3 5 -3 5]); set(gca,'FontSize',20); title('Double White Dwarfs'); legend; + xlabel('$log_{10}(M*a) [M_\odot * R_\odot]$ @ ZAMS', 'Interpreter', 'latex'); + ylabel('$log_{10}(M*a) [M_\odot * R_\odot]$ @ end', 'Interpreter', 'latex'); + + + [binariescount1, SNcount1, BHcompletecount1, SNbothcount1, SNonecount1, ... + unboundcount1, unbound1count1, unbound2count1, ... + USSNcount1, ECSNcount1, AICcount1, PISNcount1, PPISNcount1, ... + MTSNcount1, strippedSNcount1, strippednonECSNcount1, ... + strippedwindscount1, strippedRLOFcount1, strippedCEcount1, CESNcount1]=SNstats(filename1); + if(nargin==4), + [binariescount2, SNcount2, BHcompletecount2, SNbothcount2, SNonecount2, ... + unboundcount2, unbound1count2, unbound2count2, ... + USSNcount2, ECSNcount2, AICcount2, PISNcount2, PPISNcount2, ... + MTSNcount2, strippedSNcount2, strippednonECSNcount2, ... + strippedwindscount2, strippedRLOFcount2, strippedCEcount2, CESNcount2]=SNstats(filename2); + end; + if(nargin==2), + fprintf('\nSupernova statistics:\t\t\t%s\n\n', name1); + fprintf('Total number of binaries simulated:\t%d\n', binariescount1); + fprintf('Total number of supernovae:\t\t%d\n', SNcount1); + fprintf('Of these, complete collapse to BH:\t%d\n', BHcompletecount1); + fprintf('Number of binaries with two SNe:\t%d\n', SNbothcount1); + fprintf('Number of binaries with one SN:\t\t%d\n', SNonecount1); + fprintf('Number of binaries unbound by SN:\t%d\n', unboundcount1); + fprintf('Unbound by first SN:\t\t\t%d\n', unbound1count1); + fprintf('Unbound by second SN:\t\t\t%d\n\n', unbound2count1); + fprintf('Total number of USSN:\t\t\t%d\n', USSNcount1); + fprintf('Total number of ECSN:\t\t\t%d\n', ECSNcount1); + fprintf('Total number of AIC:\t\t\t%d\n', AICcount1); + fprintf('Total number of PISN:\t\t\t%d\n', PISNcount1); + fprintf('Total number of PPISN:\t\t\t%d\n\n', PPISNcount1); + fprintf('#SNe from mass-transfering progenitors:\t%d\n', MTSNcount1); + fprintf('#Stripped-envelope SNe:\t\t\t%d\n', strippedSNcount1); + fprintf('Of these, not AIC or ECSN:\t\t%d\n', strippednonECSNcount1); + fprintf('Of these, stripped by winds, no RLOF:\t%d\n', strippedwindscount1); + fprintf('Of these, # stripped by RLOF:\t\t%d\n', strippedRLOFcount1); + fprintf('Of these, previous CE:\t\t\t%d\n', strippedCEcount1); + fprintf('Or double-core CE simultaneous with SN:\t%d\n', CESNcount1); + elseif(nargin==4), + fprintf('\nSupernova statistics:\t\t\t%s\t\t%s\n\n', name1, name2); + fprintf('Total number of binaries simulated:\t%d\t\t%d\n', binariescount1, binariescount2); + fprintf('Total number of supernovae:\t\t%d\t\t%d\n', SNcount1, SNcount2); + fprintf('Of these, complete collapse to BH:\t%d\t\t%d\n', BHcompletecount1, BHcompletecount2); + fprintf('Number of binaries with two SNe:\t%d\t\t%d\n', SNbothcount1, SNbothcount2); + fprintf('Number of binaries with one SN:\t\t%d\t\t%d\n', SNonecount1, SNonecount2); + fprintf('Number of binaries unbound by SN:\t%d\t\t%d\n', unboundcount1, unboundcount2); + fprintf('Unbound by first SN:\t\t\t%d\t\t%d\n', unbound1count1, unbound1count2); + fprintf('Unbound by second SN:\t\t\t%d\t\t%d\n\n', unbound2count1, unbound2count2); + fprintf('Total number of USSN:\t\t\t%d\t\t%d\n', USSNcount1, USSNcount2); + fprintf('Total number of ECSN:\t\t\t%d\t\t%d\n', ECSNcount1, ECSNcount2); + fprintf('Total number of AIC:\t\t\t%d\t\t%d\n', AICcount1, AICcount2); + fprintf('Total number of PISN:\t\t\t%d\t\t%d\n', PISNcount1, PISNcount2); + fprintf('Total number of PPISN:\t\t\t%d\t\t%d\n\n', PPISNcount1, PPISNcount2); + fprintf('#SNe from mass-transfering progenitors:\t%d\t\t%d\n', MTSNcount1, MTSNcount2); + fprintf('#Stripped-envelope SNe:\t\t\t%d\t\t%d\n', strippedSNcount1, strippedSNcount2); + fprintf('Of these, not AIC or ECSN:\t\t%d\t\t%d\n', strippednonECSNcount1, strippednonECSNcount2); + fprintf('Of these, stripped by winds, no RLOF:\t%d\t\t%d\n', strippedwindscount1, strippedwindscount2); + fprintf('Of these, # stripped by RLOF:\t\t%d\t\t%d\n', strippedRLOFcount1, strippedRLOFcount2); + fprintf('Of these, previous CE:\t\t\t%d\t\t%d\n', strippedCEcount1, strippedCEcount2); + fprintf('Or double-core CE simultaneous with SN:\t%d\t\t%d\n', CESNcount1, CESNcount2); + end; + +end %end of ComparisonPlots + + +%Plot double compact objects; returns DCO counts, and counts of CEs leading +%to DCOs +function [BNScount, NSBHcount, BBHcount, BNSCE, NSBHCE, BBHCE, CEBBH1count] = ... + DCOplot(file, name, fignumber, colour, point) + global Msunkg G AU + type1=h5read(file,'/BSE_Double_Compact_Objects/Stellar_Type(1)'); + type2=h5read(file,'/BSE_Double_Compact_Objects/Stellar_Type(2)'); + mass1=h5read(file,'/BSE_Double_Compact_Objects/Mass(1)'); + mass2=h5read(file,'/BSE_Double_Compact_Objects/Mass(2)'); + seedDCO=h5read(file,'/BSE_Double_Compact_Objects/SEED'); + merges=h5read(file,'/BSE_Double_Compact_Objects/Merges_Hubble_Time'); + a=h5read(file,'/BSE_Double_Compact_Objects/SemiMajorAxis@DCO'); + e=h5read(file,'/BSE_Double_Compact_Objects/Eccentricity@DCO'); + mergingBBH=(type1==14) & (type2==14) & merges; + BBH=(type1==14) & (type2==14); + mergingBNS=(type1==13) & (type2==13) & merges; + BNS=(type1==13) & (type2==13); + mergingNSBH=(((type1==13) & (type2==14)) | ((type1==14) & (type2==13))) & merges; + NSBH=(((type1==13) & (type2==14)) | ((type1==14) & (type2==13))); + mergingDCO=mergingBNS | mergingNSBH | mergingBBH; + BNScount=sum(mergingBNS); NSBHcount=sum(mergingNSBH); BBHcount=sum(mergingBBH); + chirpmass=mass1.^0.6.*mass2.^0.6./(mass1+mass2).^0.2; + q=mass2./mass1; + seedCE=h5read(file,'/BSE_Common_Envelopes/SEED'); + [isCE,CEIndex]=ismember(seedDCO,seedCE); + optCE=h5read(file,'/BSE_Common_Envelopes/Optimistic_CE'); + RLOFCE=h5read(file,'/BSE_Common_Envelopes/Immediate_RLOF>CE'); + OKCE=zeros(size(mergingDCO)); OKCE(CEIndex==0)=1; OKCE(CEIndex>0)=(~optCE(CEIndex(CEIndex>0))) & (~RLOFCE(CEIndex(CEIndex>0))); + BNSCE=sum(mergingBNS & isCE & OKCE); NSBHCE=sum(mergingNSBH & isCE & OKCE); BBHCE=sum(mergingBBH & isCE & OKCE); + type1CE = h5read(file,'/BSE_Common_Envelopes/Stellar_Type(1)MT'); + isinRLOF2RLOF=h5read(file,'/BSE_RLOF/RLOF(2)>MT'); + wasinRLOF1RLOF=h5read(file,'/BSE_RLOF/RLOF(1)15 & star2RLOF==14); + %if this is zero (no such systems), can ignore this possibility, focus on the other one + if(sum(relevantbinaryFrom1)>0) fprintf('Number of HMXBs with second-born BH is non-zero (%d), please check\n', sum(relevantbinaryFrom1)); end; + relevantbinaryFrom2=(isinRLOF2RLOF & ~wasinRLOF1RLOF & ~wasinRLOF2RLOF & star2RLOF==1 & M2RLOF>15 & star1RLOF==14); + MBH=[M1RLOF(relevantbinaryFrom2); M2RLOF(relevantbinaryFrom1)]; + MO=[M2RLOF(relevantbinaryFrom2); M1RLOF(relevantbinaryFrom1)]; + %this misses binaries in which the companion overflows its RL shortly after + %evolving onto the HG; + %Now check the binaries at the time of the switch onto the HG, are they + %at least 80% RL filling at that point, if so, include them + M1Switch=h5read(file,'/BSE_Switch_Log/Mass(1)'); + M2Switch=h5read(file,'/BSE_Switch_Log/Mass(2)'); + whichSwitch=h5read(file,'/BSE_Switch_Log/Star_Switching'); + star1Switch=h5read(file,'/BSE_Switch_Log/Stellar_Type(1)'); + star2Switch=h5read(file,'/BSE_Switch_Log/Stellar_Type(2)'); + fromSwitch=h5read(file,'/BSE_Switch_Log/Switching_From'); + toSwitch=h5read(file,'/BSE_Switch_Log/Switching_To'); + roche1Switch=h5read(file,'/BSE_Switch_Log/RocheLobe(1)'); + roche2Switch=h5read(file,'/BSE_Switch_Log/RocheLobe(2)'); + radius1Switch=h5read(file,'/BSE_Switch_Log/Radius(1)'); + radius2Switch=h5read(file,'/BSE_Switch_Log/Radius(2)'); + isMergerSwitch=h5read(file,'/BSE_Switch_Log/Is_Merger'); + relevantBinaryFrom1 = whichSwitch==1 & fromSwitch==1 & toSwitch==2 & star2Switch==14 & radius1Switch>0.8*roche1Switch & M1Switch>15 & ~isMergerSwitch; + relevantBinaryFrom2 = whichSwitch==2 & fromSwitch==1 & toSwitch==2 & star1Switch==14 & radius2Switch>0.8*roche2Switch & M2Switch>15 & ~isMergerSwitch; + MBH=[MBH; M2Switch(relevantBinaryFrom1); M1Switch(relevantBinaryFrom2)]; + MO=[MO; M1Switch(relevantBinaryFrom2); M2Switch(relevantBinaryFrom1)]; + figure(fignumberHMXB), scatter(MBH, MO, point, 'filled', colour, 'DisplayName', name); hold on; +end %end of HMXBplot + + + +%Plot BeXRBs +%BeXRBs at time of SN consist of an NS and a MS B (or O) star that previously experienced +%mass accretion to spin it up. No other separation threshold is applied. +function BeXRBplot(file, name, fignumberBeXRB, colour, point) + starSNSN=h5read(file,'/BSE_Supernovae/Stellar_Type(SN)'); + starCPSN=h5read(file,'/BSE_Supernovae/Stellar_Type(CP)'); + MCPSN=h5read(file,'/BSE_Supernovae/Mass(CP)'); + unboundSN=h5read(file,'/BSE_Supernovae/Unbound'); + timeSN=h5read(file,'/BSE_Supernovae/Time'); + seedSN=h5read(file,'/BSE_Supernovae/SEED'); + seedRLOF=h5read(file,'/BSE_RLOF/SEED'); + timeRLOF=h5read(file,'/BSE_RLOF/Time>MT'); + seedCE=h5read(file,'/BSE_Common_Envelopes/SEED'); + timeCE=h5read(file,'/BSE_Common_Envelopes/Time'); + [experiencedRLOF,indexRLOF]=ismember(seedSN,seedRLOF); + precededbyRLOF=false(size(indexRLOF)); + for(i=1:length(indexRLOF)), if(experiencedRLOF(i)), precededbyRLOF(i)=(timeRLOF(indexRLOF(i))3 & ~unboundSN & experiencedRLOF & precededbyRLOF; %haven't checked direction of RLOF + figure(fignumberBeXRB), scatter(MCPSN(relevantbinary & isCE), timeSN(relevantbinary & isCE), ... + point, 'filled', colour, 'DisplayName', ['CE, ', name]); hold on; + scatter(MCPSN(relevantbinary & ~isCE), timeSN(relevantbinary & ~isCE), ... + point, colour, 'DisplayName', ['Stable, ', name]); +end %end of BeXRBplot + + + +%Plot LMXBs and IMXBs +%Find all LMXBs/IMXBs at the time of the first mass transfer episode onto the +%compact object. They consist of an NS or a BH and a companion with a mass +%below 5 Msun. +function [LMXBcount, NSLMXBcount] = LMXBplot(file, name, fignumberLMXB, colour, point) + seedRLOF=h5read(file,'/BSE_RLOF/SEED'); + timeRLOF=h5read(file,'/BSE_RLOF/Time>MT'); + M1RLOF=h5read(file,'/BSE_RLOF/Mass(1)MT'); + seedCE=h5read(file,'/BSE_Common_Envelopes/SEED'); + timeCE=h5read(file,'/BSE_Common_Envelopes/Time'); + relevantbinary=(star1RLOF>=13 & star1RLOF<=14 & star2RLOF==1 & M2RLOF<=5 & ~RLOFisCE) ... + | (star2RLOF>=13 & star2RLOF<=14 & star1RLOF==1 & M1RLOF<=5 & ~RLOFisCE); + relevantseedRLOF=cast(seedRLOF, 'int64'); relevantseedRLOF(~relevantbinary)=-1; + uniqueseeds=unique(seedRLOF(relevantbinary)); + [blah,indexlist]=ismember(cast(uniqueseeds,'int64'), relevantseedRLOF); + M1relevant=M1RLOF(indexlist); M2relevant=M2RLOF(indexlist); + MCO=M1relevant; MCO(star2RLOF(indexlist)>=13)=M2relevant(star2RLOF(indexlist)>=13); + Mcomp=M1relevant; Mcomp(star2RLOF(indexlist)==1)=M2relevant(star2RLOF(indexlist)==1); + LMXBcount=length(MCO); NSLMXBcount=sum(MCO<2); %number of LMXBs, NS LXMBs + [isCE,CEIndex]=ismember(uniqueseeds,seedCE); %doesn't check if CE happened before or after + precededbyCE=false(size(indexlist)); + for(i=1:length(indexlist)), if(isCE(i)), precededbyCE(i)=(timeCE(CEIndex(i)) < timeRLOF(indexlist(i))); end; end; + figure(fignumberLMXB), hold on; + scatter(MCO(precededbyCE), Mcomp(precededbyCE), point, 'filled', colour, 'DisplayName', ['CE, ', name]); + scatter(MCO(~precededbyCE), Mcomp(~precededbyCE), point, colour, 'DisplayName', ['Stable, ', name]); +end %end of LMXBplot + + +%Plot a*Mtot for white dwarfs +%Should be constant for non-mass-transferring WDs in the absence of tides +%and GR (and supernovae, which is why we focus on WDs as a sanity check +function DWDplot(file, name, fignumberDWD, colourNMT, colourMT, point) + global AU Rsun + M1ZAMS=h5read(file, '/BSE_System_Parameters/Mass@ZAMS(1)'); + M2ZAMS=h5read(file, '/BSE_System_Parameters/Mass@ZAMS(2)'); + aZAMS=h5read(file, '/BSE_System_Parameters/SemiMajorAxis@ZAMS'); + SeedZAMS=h5read(file, '/BSE_System_Parameters/SEED'); + Type1=h5read(file, '/BSE_Switch_Log/Stellar_Type(1)'); + Type2=h5read(file, '/BSE_Switch_Log/Stellar_Type(2)'); + M1Switch=h5read(file, '/BSE_Switch_Log/Mass(1)'); + M2Switch=h5read(file, '/BSE_Switch_Log/Mass(2)'); + aSwitch=h5read(file, '/BSE_Switch_Log/SemiMajorAxis'); + SeedSwitch=h5read(file, '/BSE_Switch_Log/SEED'); + isMergerSwitch=h5read(file,'/BSE_Switch_Log/Is_Merger'); + WDIndex=find(Type1>=10 & Type1<=12 & Type2>=10 & Type2<=12 & ~isMergerSwitch); + ind=unique(SeedSwitch(WDIndex)); + [~,ZAMSIndex]=ismember(ind,SeedZAMS); + MAZAMS=(M1ZAMS(ZAMSIndex)+M2ZAMS(ZAMSIndex)).*aZAMS(ZAMSIndex); + MAWD=(M1Switch(WDIndex)+M2Switch(WDIndex)).*aSwitch(WDIndex)*Rsun/AU; + SeedRLOF=h5read(file, '/BSE_RLOF/SEED'); + [hadRLOF,RLOFIndex]=ismember(ind,SeedRLOF); + figure(fignumberDWD), hold on; + scatter(log10(MAZAMS(~hadRLOF)), log10(MAWD(~hadRLOF)), point, 'filled', colourNMT, 'DisplayName', ['DWDs without mass transfer, ', name]); + scatter(log10(MAZAMS(hadRLOF)), log10(MAWD(hadRLOF)), point, colourMT, 'DisplayName', ['DWDs after mass transfer, ', name]); +end %end of DWDplot + + + +%SN varieties -- just count +function [binariescount, SNcount, BHcompletecount, SNbothcount, SNonecount, ... + unboundcount, unbound1count, unbound2count, ... + USSNcount, ECSNcount, AICcount, PISNcount, PPISNcount, ... + MTSNcount, strippedSNcount, strippednonECSNcount, ... + strippedwindscount, strippedRLOFcount, strippedCEcount, CESNcount] = SNstats(file) + starSNSN=h5read(file,'/BSE_Supernovae/Stellar_Type(SN)'); + starCPSN=h5read(file,'/BSE_Supernovae/Stellar_Type(CP)'); + MSNSN=h5read(file,'/BSE_Supernovae/Mass(SN)'); + unboundSN=h5read(file,'/BSE_Supernovae/Unbound'); + MpreSN=h5read(file,'/BSE_Supernovae/Mass_Total@CO(SN)'); + IsStrippedSN=h5read(file,'/BSE_Supernovae/Is_Hydrogen_Poor(SN)'); + HadRLOFSN=h5read(file,'/BSE_Supernovae/Experienced_RLOF(SN)'); + SNtypeSN=h5read(file,'/BSE_Supernovae/SN_Type(SN)'); + timeSN=h5read(file,'/BSE_Supernovae/Time'); + seedSN=h5read(file,'/BSE_Supernovae/SEED'); + prevtypeSN=h5read(file,'/BSE_Supernovae/Stellar_Type_Prev(SN)'); + seedRLOF=h5read(file,'/BSE_RLOF/SEED'); + timeRLOF=h5read(file,'/BSE_RLOF/Time>MT'); + seedCE=h5read(file,'/BSE_Common_Envelopes/SEED'); + timeCE=h5read(file,'/BSE_Common_Envelopes/Time'); + seedAll=h5read(file, '/BSE_System_Parameters/SEED'); + [isCE,indexCE]=ismember(seedSN,seedCE); + [isCE,lastindexCE]=ismember(seedSN,seedCE,'legacy'); %last index of CE seed matching given SN seed (for binaries with 2+ CE events) + simultaneouswithCE = false(size(indexCE)); + simultaneouswithCE(isCE) = (timeCE(indexCE(isCE)) == timeSN(isCE)) | (timeCE(lastindexCE(isCE)) == timeSN(isCE)); + %Note: could (very rarely) miss a coincidence if >2 CE events and only intermediate CE matches SN in time + precedingCE = false(size(indexCE)); + precedingCE(isCE) = (timeCE(indexCE(isCE)) < timeSN(isCE)); + [experiencedRLOF,indexRLOF]=ismember(seedSN,seedRLOF); + [experiencedRLOF,lastindexRLOF]=ismember(seedSN,seedRLOF, 'legacy'); + simultaneouswithRLOF = false(size(indexRLOF)); + simultaneouswithRLOF(experiencedRLOF) = (timeRLOF(indexRLOF(experiencedRLOF)) == timeSN(experiencedRLOF) ... + | timeRLOF(lastindexRLOF(experiencedRLOF)) == timeSN(experiencedRLOF)); + %Note: could miss a coincidence if >2 RLOF events and only intermediate RLOF matches SN in time + binariescount=length(seedAll); + SNcount=length(seedSN); + BHcompletecount=sum(starSNSN==14 & MpreSN==MSNSN); + SNbothcount=length(seedSN)-length(unique(seedSN)); + SNonecount=2*length(unique(seedSN))-length(seedSN); + firstSN=(starCPSN~=13 & starCPSN~=14); + secondSN=(starCPSN==13 | starCPSN==14); + unboundcount=length(unique(seedSN(unboundSN==1))); + unbound1count=sum(unboundSN(firstSN)); + unbound2count=length(unique(seedSN(unboundSN==1))) - sum(unboundSN(firstSN)); + USSNcount=sum(SNtypeSN==16); + ECSNcount=sum(SNtypeSN==2); + AICcount=sum(SNtypeSN==32); + PISNcount=sum(SNtypeSN==4); + PPISNcount=sum(SNtypeSN==8); + MTSNcount=sum(HadRLOFSN); + strippedSNcount=sum(IsStrippedSN); + strippednonECSNcount=sum(IsStrippedSN & SNtypeSN~=2 & SNtypeSN~=32); + strippedwindscount=sum(IsStrippedSN & ~HadRLOFSN & SNtypeSN~=2 & SNtypeSN~=32); + strippedRLOFcount=sum(IsStrippedSN & HadRLOFSN & prevtypeSN>=7 & prevtypeSN<=9 & SNtypeSN~=2); + strippedCEcount=sum(IsStrippedSN & HadRLOFSN & precedingCE & SNtypeSN~=2 & SNtypeSN~=32); + CESNcount=sum(IsStrippedSN & HadRLOFSN & simultaneouswithCE & SNtypeSN~=2 & SNtypeSN~=32); +end %end of SNstats + + + diff --git a/compas_matlab_utils/CosmicHistoryIntegrator.m b/compas_matlab_utils/CosmicHistoryIntegrator.m new file mode 100644 index 000000000..8b2dd4bfb --- /dev/null +++ b/compas_matlab_utils/CosmicHistoryIntegrator.m @@ -0,0 +1,358 @@ +function [SFR, Zlist, Mtlist, etalist, FormationRateByRedshiftByZ, FormationRateByRedshiftByMtByEta, ... + MergerRateByRedshiftByZ, MergerRateByRedshiftByMtByEta, zlistdetection, Rdetections, DetectableMergerRate]=... + CosmicHistoryIntegrator(filename, zlistformation, zmaxdetection, Msimulated, makeplots) +% Integrator for the binary black hole merger rate over cosmic history +% COMPAS (Compact Object Mergers: Population Astrophysics and Statistics) +% software package +% +% USAGE: +% [SFR, Zlist, Mtlist, etalist, FormationRateByRedshiftByZ, FormationRateByRedshiftByMtByEta, ... +% MergerRateByRedshiftByZ, MergerRateByRedshiftByMtByEta, zlistdetection, Rdetections, DetectableMergerRate]]=... +% CosmicHistoryIntegrator(filename, zlistformation, zmaxdetection, Msimulated [,makeplots]) +% +% INPUTS: +% filename: name of population synthesis input file +% should be in COMPAS output h5 format +% zlistformation: vector of redshifts at which the formation rate is +% computed +% zmaxdetection: maximum redshift to which the detection rate is computed +% Msimulated: total star forming mass represented by the simulation (for +% normalisation) +% makeplots: if set to 1, generates a set of useful plots (default = 0) +% +% OUTPUTS: +% SFR is a vector of size length(zlistformation) containing the star formation rate +% (solar masses per Mpc^3 of comoving volume per year of source time) +% Zlist is a vector of metallicities, taken from the COMPAS run input file +% Mtlist is a list of total mass bins +% etalist is a list of symmetric mass ratio bins +% FormationRateByRedshiftByZ is a matrix of size length(zformationlist) X length(Zlist) +% which contains a formation rate of merging double compact objects in the given redshift +% and metallicity bin, in units of formed DCOs per Mpc^3 of comoving volume per +% year of source time +% FormationRateByRedshiftByMtByEta is a matrix of size length(zformationlist) +% X length(Mtlist) X length(etalist) which contains a formation rate of merging double compact objects +% in the given redshift, total mass and eta bin, in units of formed DCOs per Mpc^3 +% of comoving volume per year of source time +% MergerRateByRedshiftByZ is a matrix of size length(zformationlist) X length(Zlist) +% which contains a merger rate of double compact objects in the given redshift +% and metallicity bin, in units of mergers per Mpc^3 of comoving volume per +% year of source time +% MergerRateByRedshiftByMtByEta is a matrix of size length(zformationlist) +% X length(Mtlist) X length(etalist) which contains a merger rate of double compact objects +% in the given redshift, total mass and eta bin, in units of mergers per Mpc^3 +% of comoving volume per year of source time +% zlistdetection is a vector of redshifts at which detection rates are +% computed (a subset of zlistformation going up to zmaxdetection) +% Rdetection is a matrix of size length(zlistdetection) X length(Mtlist) X +% length(etalist) containing the detection rate per year of observer time +% from a given redshift bin and total mass and symmetric mass ratio pixel +% DetectableMergerRate is a matrix of the same size as Rdetection but +% containing the intrinsic rate of detectable mergers per Mpc^3 of comoving +% volume per year of source time + +% +% EXAMPLE: +% zlist=0:0.01:10; +% [SFR, Zlist, Mtlist, etalist, FormationRateByRedshiftByZ, FormationRateByRedshiftByMtByEta, ... +% MergerRateByRedshiftByZ, MergerRateByRedshiftByMtByEta, zlistdetection, Rdetections, DetectableMergerRate]=... +% CosmicHistoryIntegrator('~/Work/COMPASresults/runs/Zdistalpha1-031803.h5', zlist, 1.5, 90e6, 1); +% figure(10), semilogy(zlist, sum(MergerRateByRedshiftByZ,2)*1e9,'LineWidth',3), set(gca,'FontSize',20), +% xlabel('Redshift z'), ylabel('Formation rate of merging DCO per Gpc^3 per yr') +% + + +%define constants +global Mpcm; +global Mpc; +global yr; +Mpcm=1*10^6 * 3.0856775807e16; %Mpc in meters +c=299792458; %speed of light, m/s +Mpc=Mpcm/c; %Mpc in seconds +yr=3.15569e7; %year in seconds + +if (nargin<4) + error('Not enough input arguments.'); +end; +if (nargin<5), makeplots=0; end; + +%cosmology calculator +[tL,Dl,dVc]=Cosmology(zlistformation); +%load COMPAS data +[M1,M2,Z,Tdelay,maxNS]=DataRead(filename); +%metallicity-specific SFR +[SFR,Zlist,Zweight]=Metallicity(zlistformation,min(Z),max(Z)); + +%Consider the contribution of every simulated binary to the merger rate +%in every redshift bin by considering when it would have to be formed to +%merge at that redshift and normalizing by the relevant +%metallicity-specific star formation rate +dz=zlistformation(2)-zlistformation(1); +etalist=0.01:0.01:0.25; +Mtlist=1:1:ceil(max(M1+M2)); +FormationRateByRedshiftByZ=zeros(length(zlistformation),length(Zlist)); +FormationRateByRedshiftByMtByEta=zeros(length(zlistformation),length(Mtlist),length(etalist)); +MergerRateByRedshiftByZ=zeros(length(zlistformation),length(Zlist)); +MergerRateByRedshiftByMtByEta=zeros(length(zlistformation),length(Mtlist),length(etalist)); +x=zeros(size(M1)); +for(i=1:length(M1)), + Zcounter=find(Zlist>=Z(i),1); + eta=M1(i)*M2(i)/(M1(i)+M2(i))^2; + etaindex=ceil(eta*100); + Mtindex=ceil(M1(i)+M2(i)); + FormationRateByRedshiftByZ(:,Zcounter)=transpose(SFR).*Zweight(:,Zcounter)/Msimulated; + FormationRateByRedshiftByMtByEta(:,Mtindex,etaindex)=transpose(SFR).*Zweight(:,Zcounter)/Msimulated; + tLform=tL+Tdelay(i); %lookback time of when binary would have to form in order to merge at lookback time tL + firsttooearlyindex=find((tLform)>max(tL),1); + if(isempty(firsttooearlyindex)), firsttooearlyindex=length(tL)+1; end; + zForm=interp1(tL,zlistformation,tLform(1:firsttooearlyindex-1)); + zFormindex=ceil((zForm-zlistformation(1))./dz)+1; + if(~isempty(zFormindex)) + x(i)=SFR(zFormindex(1))*Zweight(zFormindex(1),Zcounter)/Msimulated; + MergerRateByRedshiftByZ(1:firsttooearlyindex-1,Zcounter)=... + MergerRateByRedshiftByZ(1:firsttooearlyindex-1,Zcounter)+... + transpose(SFR(zFormindex)).*Zweight(zFormindex,Zcounter)/Msimulated; + MergerRateByRedshiftByMtByEta(1:firsttooearlyindex-1,Mtindex,etaindex) =... + MergerRateByRedshiftByMtByEta(1:firsttooearlyindex-1,Mtindex,etaindex) + ... + transpose(SFR(zFormindex)).*Zweight(zFormindex,Zcounter)/Msimulated; + end; +end; + +zlistdetection=zlistformation(1:find(zlistformation<=zmaxdetection,1,"last")); +fin=load('~/Work/Rai/LIGOfuture_data/freqVector.txt'); +%noise=load('~/Work/Rai/LIGOfuture_data/dataNomaLIGO.txt'); +noise=load('~/Work/Rai/LIGOfuture_data/dataEarly_low.txt'); +[Rdetections,DetectableMergerRate]=... + DetectionRate(zlistformation,Mtlist,etalist,MergerRateByRedshiftByMtByEta,zlistdetection,fin,noise,Dl,dVc); + +if(makeplots==1), %make a set of default plots + MakePlots(M1,M2,Z,Tdelay,zlistformation,Zlist,SFR,Zweight,... + MergerRateByRedshiftByZ, Rdetections, DetectableMergerRate, zlistdetection, Mtlist, etalist, 1); +end; + +end %end of CosmicHistoryIntegrator + + +%Load the data stored in COMPAS .h5 output format from a file +%Select only double compact object mergers of interest, and return the +%component masses, metallicities, and star formation to merger delay times +function [M1,M2,Z,Tdelay, maxNS]=DataRead(file) + if(exist(file, 'file')~=2), + error('Input file does not exist'); + end; + type1=h5read(file,'/BSE_Double_Compact_Objects/Stellar_Type(1)'); + type2=h5read(file,'/BSE_Double_Compact_Objects/Stellar_Type(2)'); + mass1=h5read(file,'/BSE_Double_Compact_Objects/Mass(1)'); + mass2=h5read(file,'/BSE_Double_Compact_Objects/Mass(2)'); + seedDCO=h5read(file,'/BSE_Double_Compact_Objects/SEED'); + merges=h5read(file,'/BSE_Double_Compact_Objects/Merges_Hubble_Time'); + a=h5read(file,'/BSE_Double_Compact_Objects/SemiMajorAxis@DCO'); + e=h5read(file,'/BSE_Double_Compact_Objects/Eccentricity@DCO'); + Ttotal=(h5read(file,'/BSE_Double_Compact_Objects/Time')+h5read(file,'/BSE_Double_Compact_Objects/Coalescence_Time'))*1e6; %to years + %mergingBBH=(type1==14) & (type2==14) & merges; + %BBH=(type1==14) & (type2==14); + %mergingBNS=(type1==13) & (type2==13) & merges; + %BNS=(type1==13) & (type2==13); + %mergingNSBH=(((type1==13) & (type2==14)) | ((type1==14) & (type2==13))) & merges; + %NSBH=(((type1==13) & (type2==14)) | ((type1==14) & (type2==13))); + %mergingDCO=mergingBNS | mergingNSBH | mergingBBH; + %BNScount=sum(mergingBNS); NSBHcount=sum(mergingNSBH); BBHcount=sum(mergingBBH); + maxNS=max(max(mass1(type1==13)), max(mass2(type2==13))); + chirpmass=mass1.^0.6.*mass2.^0.6./(mass1+mass2).^0.2; + q=mass2./mass1; + seedCE=h5read(file,'/BSE_Common_Envelopes/SEED'); + [isCE,CEIndex]=ismember(seedDCO,seedCE); + optCE=h5read(file,'/BSE_Common_Envelopes/Optimistic_CE'); + RLOFCE=h5read(file,'/BSE_Common_Envelopes/Immediate_RLOF>CE'); + OKCE=zeros(size(seedDCO)); OKCE(CEIndex==0)=1; OKCE(CEIndex>0)=(~optCE(CEIndex(CEIndex>0))) & (~RLOFCE(CEIndex(CEIndex>0))); + %BNSCE=sum(mergingBNS & isCE & OKCE); NSBHCE=sum(mergingNSBH & isCE & OKCE); BBHCE=sum(mergingBBH & isCE & OKCE); + mergingDCO=merges & OKCE; + Zsys=h5read(file,'/BSE_System_Parameters/Metallicity@ZAMS(1)'); + seedsys=h5read(file,'/BSE_System_Parameters/SEED'); + [blah,sysIndex]=ismember(seedDCO,seedsys); + Zdco=Zsys(sysIndex); + M1=mass1(mergingDCO); M2=mass2(mergingDCO); Z=Zdco(mergingDCO); Tdelay=Ttotal(mergingDCO); +end %end of DataRead + +%Compute the lookback time (yr), lumionosity distance (Mpc), and comoving +%volume (Mpc^3) for an array of redshifts +function [tL, Dl, dVc]=Cosmology(zvec) + global Mpcm + global Mpc + global yr + %zmax=10; dz=0.001; zvec=0:dz:zmax; + Nz=length(zvec); dz=zvec(2)-zvec(1); + + %Planck cosmology + OmegaM=0.236+0.046; %2012arXiv1212.5226H + OmegaL=0.718; + Ho=100*0.697*1000/Mpcm; %in sec; H=69.7 + Dh=1/Ho; + E=sqrt(OmegaM.*(1+zvec).^3+OmegaL); %Hogg, astro-ph/9905116, Eq. 14 + Dc=Dh*dz*cumsum(1./E); %Hogg, Eq. 15 + Dm=Dc; %Hogg, Eq. 16, k=0; + Dl=(1+zvec).*Dm/Mpc; %Hogg, Eq. 20 + %see also Eq. (1.5.46) in Weinberg, "Cosmology", 2008 + dVc=4*pi*Dh^3*(OmegaM*(1+zvec).^3+OmegaL).^(-0.5).*(Dc/Dh).^2*dz/Mpc^3; + Vc=cumsum(dVc); + dtL=(1/Ho)*dz./(1+zvec)./E/yr; %lookback time, (30) of Hogg + tL=cumsum(dtL); +end %end of Cosmology + + +%Compute the weight of each star-forming metallicity as a function of redshift +function [SFR,Zvec,Zweight]=Metallicity(zvec,minZ,maxZ) + %M_/odot per Mpc^3 per year -- Neijssel+ 2019 preferred model + %would be SFR=0.015*(1+zvec).^2.7./(1+((1+zvec)/2.9).^5.6) in Madau & Dickinson, 2014, (15) + SFR=0.01*(1+zvec).^2.77./(1+((1+zvec)/2.9).^4.7); + if(maxZ>minZ), + Zmean=0.035.*10.^(-0.23*zvec); + Zmu=log(Zmean)-0.39^2/2; + dlogZ=0.1; + logZvec=-12:dlogZ:0; %natural log + dPdlogZ=1/0.39/sqrt(2*pi)*exp(-(logZvec'-Zmu).^2/2/0.39^2); %size length(logZvec) x length(zvec) + dPdlogZ=dPdlogZ./(sum(dPdlogZ,1)*dlogZ); %normalise + minlogZindex=find(exp(logZvec)>=minZ,1, 'first'); + maxlogZindex=find(exp(logZvec)>=maxZ,1, 'first'); + Zrange=logZvec(maxlogZindex)-logZvec(minlogZindex); %ugly correction for not including tails + PdrawZ=1/Zrange; + Zvec=exp(logZvec(minlogZindex:maxlogZindex)); + Zweight=zeros(length(zvec),length(Zvec)); + dPdlogZ(minlogZindex,:)=dPdlogZ(minlogZindex,:)+sum(dPdlogZ(1:minlogZindex-1,:),1)*dlogZ/dlogZ; + dPdlogZ(maxlogZindex,:)=dPdlogZ(maxlogZindex,:)+sum(dPdlogZ(maxlogZindex+1:end,:),1)*dlogZ/dlogZ; + dPdlogZ(1:minlogZindex-1,:)=0; dPdlogZ(maxlogZindex+1:size(dPdlogZ,1),:)=0; + dPdlogZ=dPdlogZ./(sum(dPdlogZ,1)*dlogZ); %normalise + for(i=1:length(Zvec)) + index=find(exp(logZvec)>=Zvec(i), 1, 'first'); + Zweight(:,i)=dPdlogZ(index,:)./PdrawZ; %weight is desired over sampled probabiluty + end; + else %relevant for single-metallicity runs -- just give all binaries the same unit weight + Zvec=minZ; + Zweight=ones(length(zvec),1); + end; +end %end of Metallicity + + +%Compute detection rates per year of observer time and per year of source time +%per Mpc^3 of comoving volume as a function of total mass and eta +function [Rdetections, DetectableMergerRate]=... + DetectionRate(zlistformation,Mtlist,etalist,MergerRateByRedshiftByMtByEta,zlistdetection,freqfile,noisefile,Dl,dVc) + + fin=load('~/Work/Rai/LIGOfuture_data/freqVector.txt'); + noise=load('~/Work/Rai/LIGOfuture_data/dataMid_low.txt'); + + flow=10; + df=1; + f=flow:df:500; %BBH focussed + Sf=interp1(fin, noise.^2, f); + + Ntheta=1e6; + psi=rand(1,Ntheta)*pi; + phi=rand(1,Ntheta)*2*pi; + costh=rand(1,Ntheta); + cosiota=rand(1,Ntheta); + sinth=sqrt(1-costh.^2); + siniota=sqrt(1-cosiota.^2); + Fplus=1/2*(1+costh.^2).*cos(2*phi).*cos(2*psi)-costh.*sin(2*phi).*sin(2*psi); + Fcross=1/2*(1+costh.^2).*cos(2*phi).*sin(2*psi)+costh.*sin(2*phi).*cos(2*psi); + Theta=1/2*sqrt(Fplus.^2.*(1+cosiota.^2).^2+4*Fcross.^2.*cosiota.^2); + Thetas=sort(Theta); + + + %save time by not doing calculations beyond maximum redshifted total + %mass corresponding to detection redshift threshold + Mtzlistdetection=1:1:ceil(max(Mtlist)*(1+max(zlistdetection))); + SNRat1Mpc=zeros(length(Mtzlistdetection),length(etalist)); + for(i=1:length(Mtzlistdetection)), + for(j=1:length(etalist)), + [h,Am,psi]=IMRSAWaveform(f, Mtzlistdetection(i), etalist(j), 0, 0, 0, 1, flow); + integral=sum(4*Am.^2./Sf*df); + SNRat1Mpc(i,j)=sqrt(integral); + end; + end; + + SNR=zeros(length(zlistdetection),length(Mtlist),length(etalist)); + + for(i=1:length(zlistdetection)), + for(j=1:length(Mtlist)), + SNR(i,j,:)=SNRat1Mpc(ceil(j*(zlistdetection(i)+1)),:)./Dl(i); + end; + end; + %for(i=1:length(zlistdetection)), SNR(i,:,:)=SNRat1Mpc./Dl(i); end; + + SNR8pre=1:0.1:1000; + theta=1./SNR8pre; + pdetect=1-interp1([0,Thetas,1],[(0:Ntheta)/Ntheta,1],theta); + pdetect(1)=0; %set of measure zero to exceed threshold, but enforce just in case + + Rdetections=zeros(length(zlistdetection),length(Mtlist),length(etalist)); %Detections per unit observer time + DetectableMergerRate=zeros(length(zlistdetection),length(Mtlist),length(etalist)); %Detections per unit source time per unit Vc + SNR8=SNR/8; + pdetection=zeros(size(Rdetections)); + pdetection=pdetect(max(min(floor(SNR8*10),length(pdetect)),1)); + + DetectableMergerRate=MergerRateByRedshiftByMtByEta(1:length(zlistdetection),:,:).*pdetection; + Rdetections=DetectableMergerRate.*transpose(dVc(1:length(zlistdetection)))./(1+zlistdetection'); + +end %end of DetectionRate + +%Make a set of default plots +function MakePlots(M1,M2,Z,Tdelay,zlistformation,Zlist,SFR,Zweight,... + MergerRateByRedshiftByZ, Rdetections, DetectableMergerRate, zlistdetection, Mtlist, etazlist, fignumber) + + zvec=zlistformation; + + figure(fignumber), clf(fignumber); %,colormap jet; + plot(zvec, sum(MergerRateByRedshiftByZ,2)*1e9, 'LineWidth', 3), hold on; + plot(zvec, sum(MergerRateByRedshiftByZ(:,Zlist<=0.001),2)*1e9, 'LineWidth', 1); + plot(zvec, sum(MergerRateByRedshiftByZ(:,Zlist>0.001 & Zlist<0.01),2)*1e9, 'LineWidth', 1); + plot(zvec, sum(MergerRateByRedshiftByZ(:,Zlist>=0.01),2)*1e9, 'LineWidth', 1); hold off; + set(gca, 'FontSize', 20); %for labels + xlabel('z'), + ylabel('DCO merger rate per Gpc^3 per yr') + legend('Total rate', 'From Z<=0.001', 'From 0.001=0.01'), + disp(['Total DCO merger rate at z=0: ', ... + num2str(1e9*sum(MergerRateByRedshiftByZ(1,:))),... + ' per Gpc^3 per year']); + + figure(2); + colormap jet; + scatter(log10(M1),log10(M2),20,log(Z)/log(10),'filled'); + set(gca, 'FontSize', 20); %for labels + H=colorbar; H.Label.String='log_{10} metallicity'; + xlabel('log_{10}(M_1/M_o)'), ylabel('log_{10}(M_2/M_o)'); + + figure(3); + colormap jet; + scatter(M1+M2,log10(Tdelay/1e6),20,log10(Z),'filled'); + set(gca, 'FontSize', 20); %for labels + H=colorbar; H.Label.String='log_{10} metallicity'; + xlabel('Total DCO mass [M_o]'), ylabel('log_{10}(Tdelay/Myr)'); + + + figure(fignumber+3), clf(fignumber+3); + plot(zvec, SFR*1e9, 'LineWidth', 3); hold on; + plot(zvec, SFR'.*sum(Zweight(:,Zlist<=0.001),2)./sum(Zweight,2)*1e9, 'LineWidth', 1); + plot(zvec, SFR'.*sum(Zweight(:,Zlist>0.001&Zlist<0.01),2)./sum(Zweight,2)*1e9, 'LineWidth', 1); + plot(zvec, SFR'.*sum(Zweight(:,Zlist>=0.01),2)./sum(Zweight,2)*1e9, 'LineWidth', 1); hold off; + set(gca, 'FontSize', 20); %for labels + xlabel('z'), ylabel('Star-formation rate, M_o per Gpc^3 per yr'); + legend('Total rate', 'From Z<=0.001', 'From 0.001=0.01'), + + + figure(fignumber+4), clf(fignumber+4); + RdetectionsByzMt=sum(Rdetections,3); %sum across eta + semilogy(zlistdetection, cumsum(sum(RdetectionsByzMt,2)), 'LineWidth', 3), hold on; + semilogy(zlistdetection, cumsum(sum(RdetectionsByzMt(:,Mtlist<=5),2)), 'LineWidth', 1); + semilogy(zlistdetection, cumsum(sum(RdetectionsByzMt(:,Mtlist>5 & Mtlist<20),2)), 'LineWidth', 1); + semilogy(zlistdetection, cumsum(sum(RdetectionsByzMt(:,Mtlist>=20),2)), 'LineWidth', 1); hold off; + legend('Total rate', 'From M_t<=5 M_o', 'From 5=20 M_o'), + set(gca, 'FontSize', 20); %for labels + xlabel('z'), + ylabel('cumulative detection rate per observer yr') + + +end %end of MakePlots + + + diff --git a/compas_matlab_utils/IMRSAWaveform.m b/compas_matlab_utils/IMRSAWaveform.m new file mode 100644 index 000000000..09f145edd --- /dev/null +++ b/compas_matlab_utils/IMRSAWaveform.m @@ -0,0 +1,159 @@ +function [h, Aeff, psiEff] = IMRSAWaveform (fVec, totalMass, eta, chi, startPhase, ... + startTime, dEff, fLower) +% +% IMRSAWaveform: Generate the parametrised frequency domain BBH coalescence +% waveforms proposed in the paper P. Ajith et al (2009) +% +% usage: [h, Aeff, psiEff] = IMRSAWaveform (f, totalMass, eta, chi, startPhase, +% startTime, dEff, fLower) +% +% fVec : a vector of frequencies at which the waveform is generated +% totalMass : total mass of the binary (in solar masses) +% eta : symmetric mass ratio +% chi : spin parameter +% startPhase : start phase of the waveform (in radian) +% startTime : start time of the waveform (in seconds) +% dEff : effective distance (in Mpc) +% fLower : low-frequency cutoff (Hz) +% +% h : waveform in Fourier domain (complex vector) +% Aeff : amplitude in the Fourier domain +% psiEff : phase in the Fourier domain +% +% P. Ajith, 30.06.2010 +% +% $Id: IMRSAWaveform.m 73 2010-07-01 21:39:26Z ajith $ + + MSOLAR_TIME = 4.92579497077314e-06; + PARSEC_SEC = 1.0292712503e8; + + % parameters + M = totalMass*MSOLAR_TIME; + piM = pi*M; + d = dEff*PARSEC_SEC*1e6; + phi = startPhase; + shft = 2*pi*startTime; + delta = sqrt(1.-4.*eta); + mergPower = -2/3; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % compute the phenomenological parameters + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + psi0 = 3./(128*eta); + + psi1 = 0.; + + psi2 = 3715./756. + ... + -9.2091e+02*eta^1 + 4.9213e+02*eta^1*chi^1 + 1.3503e+02*eta^1*chi^2 + ... + 6.7419e+03*eta^2 + -1.0534e+03*eta^2*chi^1 + ... + -1.3397e+04*eta^3 ; + + psi3 = -16.*pi + 113.*chi/3. + ... + 1.7022e+04*eta^1 + -9.5659e+03*eta^1*chi^1 + -2.1821e+03*eta^1*chi^2 + ... + -1.2137e+05*eta^2 + 2.0752e+04*eta^2*chi^1 + ... + 2.3859e+05*eta^3 ; + + psi4 = 15293365./508032. - 405.*chi^2/8. + ... + -1.2544e+05*eta^1 + 7.5066e+04*eta^1*chi^1 + 1.3382e+04*eta^1*chi^2 + ... + 8.7354e+05*eta^2 + -1.6573e+05*eta^2*chi^1 + ... + -1.6936e+06*eta^3 ; + + psi5 = 0.; + + psi6 = -8.8977e+05*eta^1 + 6.3102e+05*eta^1*chi^1 + 5.0676e+04*eta^1*chi^2 + ... + 5.9808e+06*eta^2 + -1.4148e+06*eta^2*chi^1 + ... + -1.1280e+07*eta^3 ; + + psi7 = 8.6960e+05*eta^1 + -6.7098e+05*eta^1*chi^1 + -3.0082e+04*eta^1*chi^2 + ... + -5.8379e+06*eta^2 + 1.5145e+06*eta^2*chi^1 + ... + 1.0891e+07*eta^3 ; + + psi8 = -3.6600e+05*eta^1 + 3.0670e+05*eta^1*chi^1 + 6.3176e+02*eta^1*chi^2 + ... + 2.4265e+06*eta^2 + -7.2180e+05*eta^2*chi^1 + ... + -4.5524e+06*eta^3 ; + + f1 = 1. - 4.4547*(1.-chi).^0.217 + 3.521*(1.-chi).^0.26 + ... + 6.4365e-01*eta^1 + 8.2696e-01*eta^1*chi^1 + -2.7063e-01*eta^1*chi^2 + ... + -5.8218e-02*eta^2 + -3.9346e+00*eta^2*chi^1 + ... + -7.0916e+00*eta^3 ; + + f2 = (1. - 0.63*(1.-chi)^0.3)/2. + ... + 1.4690e-01*eta^1 + -1.2281e-01*eta^1*chi^1 + -2.6091e-02*eta^1*chi^2 + ... + -2.4900e-02*eta^2 + 1.7013e-01*eta^2*chi^1 + ... + 2.3252e+00*eta^3 ; + + sigma = (1. - 0.63*(1.-chi)^0.3)*(1.-chi)^0.45/4. + ... + -4.0979e-01*eta^1 + -3.5226e-02*eta^1*chi^1 + 1.0082e-01*eta^1*chi^2 + ... + 1.8286e+00*eta^2 + -2.0169e-02*eta^2*chi^1 + ... + -2.8698e+00*eta^3 ; + + f3 = 3.2361e-01 + 4.8935e-02*chi^1 + 1.3463e-02*chi^2 + ... + -1.3313e-01*eta^1 + -8.1719e-02*eta^1*chi^1 + 1.4512e-01*eta^1*chi^2 + ... + -2.7140e-01*eta^2 + 1.2788e-01*eta^2*chi^1 + ... + 4.9220e+00*eta^3 ; + + f3 = f3/piM; + f1 = f1/piM; + f2 = f2/piM; + sigma = sigma/piM; + + %fprintf('\nPhenomenological parameters:\n'); + %fprintf('psi0 = %5.4e \t psi1 = %5.4e \t psi2 = %5.4e \t psi3 = %5.4e \t psi4 = %5.4e\n',... + % psi0, psi1, psi2, psi3, psi4); + %fprintf('psi5 = %5.4e \t psi6 = %5.4e \t psi7 = %5.4e \t psi8 = %5.4e\n',... + % psi5, psi6, psi7, psi8); + %fprintf('f1 = %5.4e \t f2 = %5.4e \t f3 = %5.4e \t sigma = %5.4e\n',... + % f1, f2, f3, sigma); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % now generate the waveforms + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + % PN corrections to the inspiral amplitude + alpha2 = -323./224. + 451.*eta/168.; + alpha3 = (27./8. - 11.*eta/6.)*chi; + + % correction to the merger power law + epsilon_1 = 1.4547*chi - 1.8897; + epsilon_2 = -1.8153*chi + 1.6557; + + % normalisation constants for the merger and ring down amplitude + vMerg = power(pi*M*f1, 1./3.); + vRing = power(pi*M*f2, 1./3.); + w1 = 1. + alpha2*power(vMerg,2.) + alpha3*power(vMerg,3.); + w1 = w1/(1. + epsilon_1*vMerg + epsilon_2*vMerg*vMerg); + w2 = w1*(pi*sigma/2.)*power(f2/f1, mergPower)*(1. ... + + epsilon_1*vRing + epsilon_2*vRing*vRing); + + % amplitude scale + C = M^(5/6)*(f1)^(-7/6)*sqrt(5*eta/24)/(d*pi^(2/3)); + + Aeff = zeros(size(fVec)); + psiEff = zeros(size(fVec)); + + inspIdx = find(fVec >= fLower & fVec < f1); + mergIdx = find(fVec >= f1 & fVec < f2); + ringIdx = find(fVec >= f2 & fVec <= f3); + outOfBand = find(fVec < fLower | fVec > f3); + + v = power(pi*M*fVec, 1./3.); + + % effective amplitude - inspiral, merger and ring down + fNorm = fVec/f1; + pnCorr = 1 + alpha2*power(v(inspIdx),2.) + alpha3*power(v(inspIdx),3.); + + Aeff(inspIdx) = power(fNorm(inspIdx), -7./6.).*pnCorr; + Aeff(mergIdx) = w1*power(fNorm(mergIdx), mergPower).*(1. ... + + epsilon_1*v(mergIdx) + epsilon_2*power(v(mergIdx),2)); + + Lorentzian = sigma./(power(fVec(ringIdx)-f2, 2.) + sigma.*sigma/4.); + Aeff(ringIdx) = w2*Lorentzian/(2*pi); + + Aeff = C*Aeff; + psiEff = shft*fVec + phi + psi0*v.^-5.*(1 + psi2*v.^2 + psi3*v.^3 + ... + psi4*v.^4 + psi5*v.^5 + psi6*v.^6 + psi7*v.^7 + psi8*v.^8); + + h = Aeff.*exp(1i*(-psiEff)); + diff --git a/compas_matlab_utils/SSEDetailedOutput.m b/compas_matlab_utils/SSEDetailedOutput.m new file mode 100644 index 000000000..eff5affa7 --- /dev/null +++ b/compas_matlab_utils/SSEDetailedOutput.m @@ -0,0 +1,115 @@ +function SSEDetailedOutput(filename, nfiles) +% Carries out some basic analysis and makes plots for a single SSE run +% +% USAGE: +% SSEDetailedOutput(filename [, nfiles]) +% +% INPUTS: +% filename: name of detailed output file in COMPAS h5 format +% nfiles: optional if multiple files are being compared; in that case, +% the files are all assumed to start with filename, followed by numbers +% 0...nfiles-1, followed by .h5 +% +% examples: +% SSEDetailedOutput('~/Work/COMPAS/src/COMPAS_Output/Detailed_Output/SSE_Detailed_Output_0.h5') +% SSEDetailedOutput('~/Work/COMPAS/src/COMPAS_Output/Detailed_Output/SSE_Detailed_Output_', 5) +% + + +if(nargin==1) %parse one file + +file=filename; +time=h5read(file,'/Time'); +Z=h5read(file,'/Metallicity@ZAMS'); +mass=h5read(file,'/Mass'); +massCO=h5read(file,'/Mass_CO_Core'); +massHe=h5read(file,'/Mass_He_Core'); +luminosity=h5read(file,'/Luminosity'); +type=h5read(file,'/Stellar_Type'); +radius=h5read(file,'/Radius'); +temperature=h5read(file,'/Teff'); +mdot=h5read(file,'/Mdot'); +ml=h5read(file,'/Dominant_Mass_Loss_Rate'); +%envMass=h5read(file,'/Mass_Env'); +%convEnvMass=h5read(file,'/Mass_Convective_Env'); +%lambda=h5read(file,'/Lambda_Convective'); +%binding=h5read(file,'/BE_ConvectiveEnvelope'); +rec=h5read(file,'/Record_Type'); + + +figure(1), %HR diagram +scatter(temperature, luminosity, 20, type, 'filled'); +set(gca,'FontSize',20); xlabel('Teff, T_o'); ylabel('Luminosity, L_o'); set(gca,'XDir','reverse') +H=colorbar; H.Label.String='Stellar type'; + + +figure(2), %Radial expansion by phase +scatter(time(type==1),radius(type==1),20,'filled', 'b'); hold on; +scatter(time(type==2),radius(type==2),20,'filled', 'g'); +scatter(time(type==3),radius(type==3),20,'filled', 'y'); +scatter(time(type==4),radius(type==4),20,'filled', 'r'); +scatter(time(type==5),radius(type==5),20,'filled', 'm'); +scatter(time(type==6),radius(type==6),20,'filled', 'k'); +hold off; +set(gca,'FontSize',20); xlabel('Time, Myr'); ylabel('Radius, Rsun'), legend('MS','HG','FGB','CHeB','EAGB','TPAGB') + +%for(i=2:length(radius)), %remove points where radius is less than the radius at previous time +% if(~isempty(find(radius(1:i-1)>=radius(i)))), +% envMass(i)=0; convEnvMass(i)=0; radius(i)=0; +% end; +%end; +%goodindices=type>1 & type<=6 & radius>0; +%figure(2); +%subplot(2,1,1), scatter(radius(goodindices), envMass(goodindices)-convEnvMass(goodindices), 20, 'b', 'filled'); hold on; +%scatter(radius(goodindices), convEnvMass(goodindices), 20, 'm', 'filled'); hold on; +%scatter(radius(goodindices), mass(goodindices)-envMass(goodindices), 20, 'y', 'filled'); +%scatter(radius(goodindices), mass(goodindices), 20, 'k', 'filled'); +%hold off; +%set(gca,'FontSize',20); xlabel('Radius, Rsun'); ylabel('Mass, Msun'), legend('Radiative intershell','Convective Envelope','Core', 'Total') +%subplot(2,1,2); scatter(radius(goodindices), binding(goodindices), 20, 'm', 'filled') +%set(gca,'FontSize',20); xlabel('Radius, Rsun'); ylabel('Binding E conv env, erg') +%%subplot(2,1,2); scatter(radius(goodindices), lambda(goodindices), 20, 'm', 'filled') +%%set(gca,'FontSize',20); xlabel('Radius, Rsun'); ylabel('\lambda') + + +%radius=h5read(file,'/BSE_Common_Envelopes/Radius(1)CE'); +%apost=h5read(file,'/BSE_Common_Envelopes/SemiMajorAxis>CE'); +%primarydonor=(h5read(file,'/BSE_Common_Envelopes/RLOF(1)')==1); +%figure(3); semilogy(radius(primarydonor), apre(primarydonor), 'y-.', radius(primarydonor), a1(primarydonor), 'y--', radius(primarydonor), apost(primarydonor), 'y-', 'LineWidth', 3); hold off; +%set(gca,'FontSize',20); xlabel('Radius, Rsun'); ylabel('Semimajor axis, Rsun'), legend('Pre CE','After Stage1','After Stage 2') + +end; % end of if(nargin==1) + +if(nargin==2), + +for (k=0:nfiles-1), +file=[filename,int2str(k),'.h5']; +time=h5read(file,'/Time'); +Z=h5read(file,'/Metallicity@ZAMS'); +mass=h5read(file,'/Mass'); +massCO=h5read(file,'/Mass_CO_Core'); +massHe=h5read(file,'/Mass_He_Core'); +luminosity=h5read(file,'/Luminosity'); +type=h5read(file,'/Stellar_Type'); +radius=h5read(file,'/Radius'); +binding=h5read(file,'/BE_Nanjing'); +rec=h5read(file,'/Record_Type'); +for(i=2:length(radius)), %remove points where radius is less than one of previously achieved radii + if(~isempty(find(radius(1:i-1)>=radius(i)))), + binding(i)=0; radius(i)=0; + end; +end; +figure(k+1), +scatter(radius(type==1),log10(binding(type==1)),20,'filled'); hold on; +scatter(radius(type==2),log10(binding(type==2)),20,'filled'); +scatter(radius(type==3),log10(binding(type==3)),20,'filled'); +scatter(radius(type==4),log10(binding(type==4)),20,'filled'); +scatter(radius(type==5),log10(binding(type==5)),20,'filled'); +scatter(radius(type==6),log10(binding(type==6)),20,'filled'); +hold off; +set(gca,'FontSize',20); xlabel('Radius, Rsun'); ylabel('log_{10}(Nanjing binding energy / erg)'), legend('MS','HG','FGB','CHeB','EAGB','TPAGB'), title(['ZAMS Mass in solar masses:', int2str(mass(1))]) +end; + +end; %end of if(nargin==2) diff --git a/compas_python_utils/cosmic_integration/ClassCOMPAS.py b/compas_python_utils/cosmic_integration/ClassCOMPAS.py index 76ac552b3..ef15200f4 100644 --- a/compas_python_utils/cosmic_integration/ClassCOMPAS.py +++ b/compas_python_utils/cosmic_integration/ClassCOMPAS.py @@ -2,7 +2,11 @@ import numpy as np import h5py as h5 import os -from . import totalMassEvolvedPerZ as MPZ +import sys +# Get the COMPAS_ROOT_DIR var, and add the cosmic_integration directory to the path +compas_root_dir = os.getenv('COMPAS_ROOT_DIR') +sys.path.append(os.path.join(compas_root_dir, 'compas_python_utils/cosmic_integration')) +import totalMassEvolvedPerZ as MPZ class COMPASData(object): @@ -31,12 +35,13 @@ def __init__( self.mass2 = None # Msun self.DCOmask = None self.allTypesMask = None - self.BBHmask = None - self.DNSmask = None + self.BHBHmask = None + self.NSNSmask = None self.BHNSmask = None + self.WDWDmask = None self.CHE_mask = None - self.CHE_BBHmask = None - self.NonCHE_BBHmask = None + self.CHE_BHBHmask = None + self.NonCHE_BHBHmask = None self.initialZ = None self.sw_weights = None self.n_systems = None @@ -63,9 +68,9 @@ def __init__( print(" and optionally self.setGridAndMassEvolved() if using a metallicity grid") def setCOMPASDCOmask( - self, types="BBH", withinHubbleTime=True, pessimistic=True, noRLOFafterCEE=True + self, types="BHBH", withinHubbleTime=True, pessimistic=True, noRLOFafterCEE=True ): - # By default, we mask for BBHs that merge within a Hubble time, assuming + # By default, we mask for BHBHs that merge within a Hubble time, assuming # the pessimistic CEE prescription (HG donors cannot survive a CEE) and # not allowing immediate RLOF post-CEE @@ -73,14 +78,14 @@ def setCOMPASDCOmask( self.get_COMPAS_variables("BSE_Double_Compact_Objects", ["Stellar_Type(1)", "Stellar_Type(2)", "Merges_Hubble_Time", "SEED"]) dco_seeds = dco_seeds.flatten() - if types == "CHE_BBH" or types == "NON_CHE_BBH": + if types == "CHE_BHBH" or types == "NON_CHE_BHBH": stellar_type_1_zams, stellar_type_2_zams, che_ms_1, che_ms_2, sys_seeds = \ self.get_COMPAS_variables("BSE_System_Parameters", ["Stellar_Type@ZAMS(1)", "Stellar_Type@ZAMS(2)", "CH_on_MS(1)", "CH_on_MS(2)", "SEED"]) che_mask = np.logical_and.reduce((stellar_type_1_zams == 16, stellar_type_2_zams == 16, che_ms_1 == True, che_ms_2 == True)) che_seeds = sys_seeds[()][che_mask] - self.CHE_mask = np.in1d(dco_seeds, che_seeds) if types == "CHE_BBH" or types == "NON_CHE_BBH" else np.repeat(False, len(dco_seeds)) + self.CHE_mask = np.in1d(dco_seeds, che_seeds) if types == "CHE_BHBH" or types == "NON_CHE_BHBH" else np.repeat(False, len(dco_seeds)) # if user wants to mask on Hubble time use the flag, otherwise just set all to True, use astype(bool) to set masks to bool type hubble_mask = hubble_flag.astype(bool) if withinHubbleTime else np.repeat(True, len(dco_seeds)) @@ -88,12 +93,18 @@ def setCOMPASDCOmask( # mask on stellar types (where 14=BH and 13=NS), BHNS can be BHNS or NSBH type_masks = { "all": np.repeat(True, len(dco_seeds)), - "BBH": np.logical_and(stellar_type_1 == 14, stellar_type_2 == 14), - "BHNS": np.logical_or(np.logical_and(stellar_type_1 == 14, stellar_type_2 == 13), np.logical_and(stellar_type_1 == 13, stellar_type_2 == 14)), - "BNS": np.logical_and(stellar_type_1 == 13, stellar_type_2 == 13), + "BHBH": np.logical_and(stellar_type_1 == 14, stellar_type_2 == 14), + "NSNS": np.logical_and(stellar_type_1 == 13, stellar_type_2 == 13), + "WDWD": np.logical_and(np.isin(stellar_type_1,[10,11,12]),np.isin(stellar_type_2,[10,11,12])), + "BHNS": np.logical_or(np.logical_and(stellar_type_1 == 13, stellar_type_2 == 14),np.logical_and(stellar_type_1 == 14, stellar_type_2 == 13)), + "NSWD": np.logical_or(np.logical_and(np.isin(stellar_type_1,[10,11,12]),stellar_type_2 == 13), + np.logical_and(np.isin(stellar_type_2,[10,11,12]),stellar_type_1 == 13)), + "WDBH": np.logical_or(np.logical_and(np.isin(stellar_type_1,[10,11,12]),stellar_type_2 == 14), + np.logical_and(np.isin(stellar_type_2,[10,11,12]),stellar_type_1 == 14)), } - type_masks["CHE_BBH"] = np.logical_and(self.CHE_mask, type_masks["BBH"]) if types == "CHE_BBH" else np.repeat(False, len(dco_seeds)) - type_masks["NON_CHE_BBH"] = np.logical_and(np.logical_not(self.CHE_mask), type_masks["BBH"]) if types == "NON_CHE_BBH" else np.repeat(True, len(dco_seeds)) + + type_masks["CHE_BHBH"] = np.logical_and(self.CHE_mask, type_masks["BHBH"]) if types == "CHE_BHBH" else np.repeat(False, len(dco_seeds)) + type_masks["NON_CHE_BHBH"] = np.logical_and(np.logical_not(self.CHE_mask), type_masks["BHBH"]) if types == "NON_CHE_BHBH" else np.repeat(True, len(dco_seeds)) # if the user wants to make RLOF or optimistic CEs if noRLOFafterCEE or pessimistic: @@ -124,11 +135,14 @@ def setCOMPASDCOmask( # create a mask for each dco type supplied self.DCOmask = type_masks[types] * hubble_mask * rlof_mask * pessimistic_mask - self.BBHmask = type_masks["BBH"] * hubble_mask * rlof_mask * pessimistic_mask + self.BHBHmask = type_masks["BHBH"] * hubble_mask * rlof_mask * pessimistic_mask + self.NSNSmask = type_masks["NSNS"] * hubble_mask * rlof_mask * pessimistic_mask + self.WDWDmask = type_masks["WDWD"] * hubble_mask * rlof_mask * pessimistic_mask self.BHNSmask = type_masks["BHNS"] * hubble_mask * rlof_mask * pessimistic_mask - self.DNSmask = type_masks["BNS"] * hubble_mask * rlof_mask * pessimistic_mask - self.CHE_BBHmask = type_masks["CHE_BBH"] * hubble_mask * rlof_mask * pessimistic_mask - self.NonCHE_BBHmask = type_masks["NON_CHE_BBH"] * hubble_mask * rlof_mask * pessimistic_mask + self.WDWDmask = type_masks["NSWD"] * hubble_mask * rlof_mask * pessimistic_mask + self.WDWDmask = type_masks["WDBH"] * hubble_mask * rlof_mask * pessimistic_mask + self.CHE_BHBHmask = type_masks["CHE_BHBH"] * hubble_mask * rlof_mask * pessimistic_mask + self.NonCHE_BHBHmask = type_masks["NON_CHE_BHBH"] * hubble_mask * rlof_mask * pessimistic_mask self.allTypesMask = type_masks["all"] * hubble_mask * rlof_mask * pessimistic_mask self.optimisticmask = pessimistic_mask @@ -158,6 +172,9 @@ def setCOMPASData(self): primary_masses, secondary_masses, formation_times, coalescence_times, dco_seeds = \ self.get_COMPAS_variables("BSE_Double_Compact_Objects", ["Mass(1)", "Mass(2)", "Time", "Coalescence_Time", "SEED"]) + # Raise an error if DCO table is empty + if len(primary_masses) == 0: + raise ValueError("BSE_Double_Compact_Objects is empty!") initial_seeds, initial_Z = self.get_COMPAS_variables("BSE_System_Parameters", ["SEED", "Metallicity@ZAMS(1)"]) @@ -173,6 +190,10 @@ def setCOMPASData(self): self.mass1 = primary_masses[self.DCOmask] self.mass2 = secondary_masses[self.DCOmask] + #Check that you have some systems of interest in your DCO table (i.e. len(primary_masses[self.DCOmask])>0 ) + if len(self.mass1) == 0: + raise ValueError("No DCOs found with the current mask. Please check your DCO table, or change your mask settings.") + # Stuff of data I dont need for integral # but I might be to laze to read in myself # and often use. Might turn it of for memory efficiency diff --git a/compas_python_utils/cosmic_integration/FastCosmicIntegration.py b/compas_python_utils/cosmic_integration/FastCosmicIntegration.py index 85c6a30d8..8da39e2c5 100644 --- a/compas_python_utils/cosmic_integration/FastCosmicIntegration.py +++ b/compas_python_utils/cosmic_integration/FastCosmicIntegration.py @@ -1,18 +1,24 @@ import numpy as np import h5py as h5 import os +import sys import time import matplotlib.pyplot as plt import scipy from scipy.interpolate import interp1d from scipy.stats import norm as NormDist -from compas_python_utils.cosmic_integration import ClassCOMPAS -from compas_python_utils.cosmic_integration import selection_effects import warnings import astropy.units as u import argparse import importlib -from compas_python_utils.cosmic_integration.cosmology import get_cosmology + +# Get the COMPAS_ROOT_DIR var, and add the cosmic_integration directory to the path +compas_root_dir = os.getenv('COMPAS_ROOT_DIR') +sys.path.append(os.path.join(compas_root_dir, 'compas_python_utils/cosmic_integration')) +import ClassCOMPAS +from cosmology import get_cosmology +import selection_effects + def calculate_redshift_related_params(max_redshift=10.0, max_redshift_detection=1.0, redshift_step=0.001, z_first_SF = 10.0, cosmology=None): """ @@ -212,7 +218,7 @@ def find_formation_and_merger_rates(n_binaries, redshifts, times, time_first_SF, merger_rate[i, :first_too_early_index - 1] = formation_rate[i, z_of_formation_index] return formation_rate, merger_rate -def compute_snr_and_detection_grids(sensitivity="O1", snr_threshold=8.0, Mc_max=300.0, Mc_step=0.1, +def compute_snr_and_detection_grids(dco_type, sensitivity="O1", snr_threshold=8.0, Mc_max=300.0, Mc_step=0.1, eta_max=0.25, eta_step=0.01, snr_max=1000.0, snr_step=0.1): """ Compute a grid of SNRs and detection probabilities for a range of masses and SNRs @@ -238,6 +244,10 @@ def compute_snr_and_detection_grids(sensitivity="O1", snr_threshold=8.0, Mc_max= snr_grid_at_1Mpc --> [2D float array] The snr of a binary with masses (Mc, eta) at a distance of 1 Mpc detection_probability_from_snr --> [list of floats] A list of detection probabilities for different SNRs """ + # If DCO type includes a WD, return empty arrays since we currently only support LVK sensitivity + if dco_type in ["WDWD", "NSWD", "WDBH"]: + warnings.warn("!! Detected rate is not computed since DCO type {} doesnt work with LVK sensitivity {}".format(dco_type, sensitivity)) + # get interpolator given sensitivity interpolator = selection_effects.SNRinterpolator(sensitivity) @@ -310,7 +320,7 @@ def find_detection_probability(Mc, eta, redshifts, distances, n_redshifts_detect return detection_probability -def find_detection_rate(path, dco_type="BBH", merger_output_filename=None, weight_column=None, +def find_detection_rate(path, dco_type="BHBH", merger_output_filename=None, weight_column=None, merges_hubble_time=True, pessimistic_CEE=True, no_RLOF_after_CEE=True, max_redshift=10.0, max_redshift_detection=1.0, redshift_step=0.001, z_first_SF = 10, use_sampled_mass_ranges=True, m1_min=5 * u.Msun, m1_max=150 * u.Msun, m2_min=0.1 * u.Msun, fbin=0.7, @@ -332,7 +342,7 @@ def find_detection_rate(path, dco_type="BBH", merger_output_filename=None, weigh == Arguments for finding and masking COMPAS file == =================================================== path --> [string] Path to the COMPAS data file that contains the output - dco_type --> [string] Which DCO type to calculate rates for: one of ["all", "BBH", "BHNS", "BNS"] + dco_type --> [string] Which DCO type to calculate rates for: one of ["all", "BHBH", "NSNS", "WDWD", "BHNS", "NSWD", "WDBH"] merger_output_filename --> [string] Optional name of output file to store merging DCOs (do not create the extra output if None) weight_column --> [string] Name of column in "DoubleCompactObjects" file that contains adaptive sampling weights (Leave this as None if you have unweighted samples) @@ -393,7 +403,7 @@ def find_detection_rate(path, dco_type="BBH", merger_output_filename=None, weigh # assert that input will not produce errors assert max_redshift_detection <= max_redshift, "Maximum detection redshift cannot be below maximum redshift" assert m1_min <= m1_max, "Minimum sampled primary mass cannot be above maximum sampled primary mass" - assert np.logical_and(fbin >= 0.0, fbin <= 1.0), "Binary fraction must be between 0 and 1" + assert fbin is None or (0.0 <= fbin <= 1.0), "Binary fraction must be between 0 and 1, or if None will vary with mass" assert Mc_step < Mc_max, "Chirp mass step size must be less than maximum chirp mass" assert eta_step < eta_max, "Symmetric mass ratio step size must be less than maximum symmetric mass ratio" assert snr_step < snr_max, "SNR step size must be less than maximum SNR" @@ -469,7 +479,7 @@ def find_detection_rate(path, dco_type="BBH", merger_output_filename=None, weigh COMPAS.delayTimes, COMPAS.sw_weights) # create lookup tables for the SNR at 1Mpc as a function of the masses and the probability of detection as a function of SNR - snr_grid_at_1Mpc, detection_probability_from_snr = compute_snr_and_detection_grids(sensitivity, snr_threshold, Mc_max, Mc_step, + snr_grid_at_1Mpc, detection_probability_from_snr = compute_snr_and_detection_grids(dco_type, sensitivity, snr_threshold, Mc_max, Mc_step, eta_max, eta_step, snr_max, snr_step) # use lookup tables to find the probability of detecting each binary at each redshift @@ -529,6 +539,8 @@ def append_rates(path, detection_rate, formation_rate, merger_rate, redshifts, C print('shape redshifts', np.shape(redshifts)) print('shape COMPAS.sw_weights', np.shape(COMPAS.sw_weights) ) print('COMPAS.DCOmask', COMPAS.DCOmask, ' was set for dco_type', dco_type) + if dco_type=='all': + print('Note that rates are calculated for ALL systems in the DCO table, this could include WDWD') print('shape COMPAS COMPAS.DCOmask', np.shape(COMPAS.DCOmask) ) ################################################# @@ -550,7 +562,6 @@ def append_rates(path, detection_rate, formation_rate, merger_rate, redshifts, C else: print(new_rate_group, 'exists, we will overrwrite the data') - ################################################# # Bin rates by redshifts ################################################# @@ -579,7 +590,7 @@ def append_rates(path, detection_rate, formation_rate, merger_rate, redshifts, C N_dco_in_z_bin = (merger_rate[:,:] * fine_shell_volumes[:]) print('fine_shell_volumes', fine_shell_volumes) - # The number of merging BBHs that need a weight + # The number of merging DCO systems that need a weight N_dco = len(merger_rate[:,0]) #################### @@ -609,7 +620,7 @@ def append_rates(path, detection_rate, formation_rate, merger_rate, redshifts, C detection_index = z_index if z_index < n_redshifts_detection else n_redshifts_detection print('You will only save data up to redshift ', maxz, ', i.e. index', z_index) - save_redshifts = redshifts + save_redshifts = redshifts[:z_index] save_merger_rate = merger_rate[:,:z_index] save_detection_rate = detection_rate[:,:detection_index] @@ -619,7 +630,8 @@ def append_rates(path, detection_rate, formation_rate, merger_rate, redshifts, C # Write the rates as a separate dataset # re-arrange your list of rate parameters DCO_to_rate_mask = COMPAS.DCOmask #save this bool for easy conversion between BSE_Double_Compact_Objects, and CI weights - rate_data_list = [DCO['SEED'][DCO_to_rate_mask], DCO_to_rate_mask , save_redshifts, save_merger_rate, merger_rate[:,0], save_detection_rate] + DCO_seeds = h_new['BSE_Double_Compact_Objects']['SEED'][DCO_to_rate_mask] # Get DCO seed + rate_data_list = [DCO_seeds, DCO_to_rate_mask , save_redshifts, save_merger_rate, merger_rate[:,0], save_detection_rate] rate_list_names = ['SEED', 'DCOmask', 'redshifts', 'merger_rate','merger_rate_z0', 'detection_rate'+sensitivity] for i, data in enumerate(rate_data_list): print('Adding rate info of shape', np.shape(data)) @@ -760,20 +772,29 @@ def plot_rates(save_dir, formation_rate, merger_rate, detection_rate, redshifts, else: plt.close() - +# To allow f_binary to be None or Float +def none_or_float(value): + return None if value.lower() == "none" else float(value) def parse_cli_args(): parser = argparse.ArgumentParser() parser.add_argument("--path", dest='path', help="Path to the COMPAS file that contains the output", type=str, default="COMPAS_Output.h5") - # For what DCO would you like the rate? options: ALL, BHBH, BHNS NSNS + + # For what DCO would you like the rate? options: ALL, BHBH, BHNS NSNS, WDWD parser.add_argument("--dco_type", dest='dco_type', - help="Which DCO type you used to calculate rates, one of: ['all', 'BBH', 'BHNS', 'BNS'] ", - type=str, default="BBH") + help="Which DCO type you used to calculate rates, one of: ['all', 'BHBH', 'NSNS', 'WDWD', 'BHNS', 'NSWD', 'WDBH'] ", + type=str, default="BHBH") parser.add_argument("--weight", dest='weight_column', help="Name of column w AIS sampling weights, i.e. 'mixture_weight'(leave as None for unweighted samples) ", type=str, default=None) - + parser.add_argument("--keep_pessimistic_CEE", dest='remove_pessimistic_CEE', + help="keep_pessimistic_CEE will set remove_pessimistic_CEE to false. The default behaviour (remove_pessimistic_CEE == True), will mask binaries that experience a CEE while on the HG", + action='store_false', default=True) + parser.add_argument("--keepRLOF_postCE", dest='remove_RLOF_after_CEE', + help="keepRLOF_postCE will set remove_RLOF_after_CEE to false. The default behaviour (remove_RLOF_after_CEE == True), will mask binaries that have immediate RLOF after a CCE", + action='store_false', default=True) + # Options for the redshift evolution and detector sensitivity parser.add_argument("--maxz", dest='max_redshift', help="Maximum redshift to use in array", type=float, default=10) parser.add_argument("--zSF", dest='z_first_SF', help="redshift of first star formation", type=float, default=10) @@ -792,7 +813,7 @@ def parse_cli_args(): default=150.) parser.add_argument("--m2min", dest='m2_min', help="Minimum secondary mass sampled by COMPAS", type=float, default=0.1) - parser.add_argument("--fbin", dest='fbin', help="Binary fraction used by COMPAS", type=float, default=0.7) + parser.add_argument("--fbin", dest='fbin', help="Binary fraction used by COMPAS, if None f_bin will be changing with mass", type=none_or_float, default=0.7) # Parameters determining dP/dZ and SFR(z), default options from Neijssel 2019 parser.add_argument("--mu0", dest='mu0', help="mean metallicity at redshhift 0", type=float, default=0.035) @@ -847,6 +868,8 @@ def main(): args.path, dco_type=args.dco_type, weight_column=args.weight_column, + pessimistic_CEE=args.remove_pessimistic_CEE, + no_RLOF_after_CEE=args.remove_RLOF_after_CEE, max_redshift=args.max_redshift, max_redshift_detection=args.max_redshift_detection, redshift_step=args.redshift_step, diff --git a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/bbh_population.py b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/bbh_population.py deleted file mode 100644 index 089aebe87..000000000 --- a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/bbh_population.py +++ /dev/null @@ -1,252 +0,0 @@ -import numpy as np -import h5py - -from .gpu_utils import xp -import h5py as h5 -from typing import List, Optional - -from ..totalMassEvolvedPerZ import ( - analytical_star_forming_mass_per_binary_using_kroupa_imf, - draw_samples_from_kroupa_imf) -from .conversions import m1_m2_to_chirp_mass, m1_m2_to_eta -from .plotting import plot_bbh_population - -# Default values for BBH population -M1_MIN = 5 -M1_MAX = 150 -M2_MIN = 0.1 - - -class BBHPopulation(object): - - def __init__( - self, - m1, - m2, - t_delay, - z_zams, - n_systems, - m1_min=M1_MIN, - m1_max=M1_MAX, - m2_min=M2_MIN, - binary_fraction=0.7, - ): - self.m1_min = m1_min - self.m1_max = m1_max - self.m2_min = m2_min - self.binaryFraction = binary_fraction - self.mass_evolved_per_binary = analytical_star_forming_mass_per_binary_using_kroupa_imf( - m1_max=self.m1_max, - m1_min=self.m1_min, - m2_min=self.m2_min, - fbin=self.binaryFraction, - ) - self.m1 = m1 - self.m2 = m2 - self.t_delay = t_delay - self.z_zams = z_zams - self.n_systems = n_systems - - - - @classmethod - def from_compas_h5( - cls, - path=None, - m1_min=M1_MIN, - m1_max=M1_MAX, - m2_min=M2_MIN, - binary_fraction=0.7, - ): - """ - Loads the BBH population from COMPAS output. - Requires the COMPAS output to contain the following datasets: - Size N (number of systems) - - BSE_System_Parameters/SEED - - BSE_System_Parameters/Metallicity@ZAMS(1) - - Size N_CE (number of CE events) < N - - BSE_Common_Envelopes/SEED - - BSE_Common_Envelopes/Immediate_RLOF>CE - - BSE_Common_Envelopes/Optimistic_CE - - Size N_BBH (number of BBHs) < N - - BSE_Double_Compact_Objects/SEED - - BSE_Double_Compact_Objects/Mass(1) - - BSE_Double_Compact_Objects/Mass(2) - - BSE_Double_Compact_Objects/Time - - BSE_Double_Compact_Objects/Coalescence_Time - - BSE_Double_Compact_Objects/Stellar_Type(1) - - BSE_Double_Compact_Objects/Stellar_Type(2) - - BSE_Double_Compact_Objects/Merges_Hubble_Time - - """ - mask = BBHPopulation.__generate_mask(path) - m1, m2, t_form, t_merge, dco_seeds = _load_data( - path, "BSE_Double_Compact_Objects", - ["Mass(1)", "Mass(2)", "Time", "Coalescence_Time", "SEED"], - mask=mask - ) - all_seeds, z_zams = _load_data(path, "BSE_System_Parameters", ["SEED", "Metallicity@ZAMS(1)"]) - dco_mask = xp.in1d(all_seeds, dco_seeds) - return cls( - m1_min = m1_min, - m1_max = m1_max, - m2_min = m2_min, - binary_fraction = binary_fraction, - m1 = m1, - m2 = m2, - t_delay = t_form + t_merge, - z_zams = z_zams[dco_mask], - n_systems = len(all_seeds), - ) - - @property - def avg_sf_mass_needed(self): - return self.mass_evolved_per_binary * self.n_systems - - @property - def chirp_mass(self): - if not hasattr(self, "_chirp_mass"): - self._chirp_mass = m1_m2_to_chirp_mass(self.m1, self.m2) - return self._chirp_mass - - @property - def eta(self): - if not hasattr(self, "_eta"): - self._eta = m1_m2_to_eta(self.m1, self.m2) - return self._eta - - def __repr__(self): - return f"" - - def __str__(self): - return self.__repr__() - - @staticmethod - def __generate_mask(path): - type_1, type_2, hubble_mask, dco_seeds = _load_data( - path, "BSE_Double_Compact_Objects", - ["Stellar_Type(1)", "Stellar_Type(2)", "Merges_Hubble_Time", "SEED"] - ) - hubble_mask = hubble_mask.astype(bool) - bbh_mask = (type_1 == 14) & (type_2 == 14) - del type_1, type_2 - - # get the flags and unique seeds from the Common Envelopes file - ce_seeds, rlof_flag, optimistic_ce = _load_data( - path, "BSE_Common_Envelopes", ["SEED", "Immediate_RLOF>CE", "Optimistic_CE"]) - dco_from_ce = xp.in1d(ce_seeds, dco_seeds) - dco_ce_seeds = ce_seeds[dco_from_ce] - del ce_seeds - - # mask out all DCOs that have RLOF after CE - rlof_flag = rlof_flag[dco_from_ce].astype(bool) - rlof_seeds = xp.unique(dco_ce_seeds[rlof_flag]) - mask_out_with_rlof_seeds = xp.logical_not(xp.in1d(dco_seeds, rlof_seeds)) - del rlof_flag, rlof_seeds - - # mask out all DCOs that have an "optimistic CE" - optimistic_ce_flag = optimistic_ce[dco_from_ce].astype(bool) - optimistic_ce_seeds = xp.unique(dco_ce_seeds[optimistic_ce_flag]) - mask_out_optimistic_ce_seeds = xp.logical_not(xp.in1d(dco_seeds, optimistic_ce_seeds)) - del optimistic_ce_flag, optimistic_ce_seeds - - return bbh_mask * hubble_mask * mask_out_with_rlof_seeds * mask_out_optimistic_ce_seeds - - @property - def label(self): - return f"n{self.n_bbh}_bbh_population" - - @property - def n_bbh(self): - return len(self.m1) - - def plot(self): - return plot_bbh_population( - data=xp.asarray([self.m1, self.m2, self.chirp_mass, np.log(self.z_zams), np.log(self.t_delay)]).T, - params=[ - r"$m_1\ [M_{\odot}]$", r"$m_2\ [M_{\odot}]$", r"$\mathcal{M}_{\rm chirp}\ [M_{\odot}]$", - r"$\ln z_{\rm ZAMS}$", r"$\ln t_{\rm delay}\ [\ln {\rm Myr}]$", - ]) - - - def bootstrap_population(self): - """Artificially generate a new population by drawing from the original population with replacement""" - n_bbh = np.random.poisson(self.n_bbh) - idx = np.random.choice(np.arange(self.n_bbh), size=n_bbh, replace=True) - return BBHPopulation( - m1=self.m1[idx], - m2=self.m2[idx], - t_delay=self.t_delay[idx], - z_zams=self.z_zams[idx], - n_systems=self.n_systems, - m1_min=self.m1_min, - m1_max=self.m1_max, - m2_min=self.m2_min, - binary_fraction=self.binaryFraction, - ) - - -def _load_data(path: str, group: str, var_names: List[str], mask: Optional[xp.ndarray] = None): - """Load variables from a hdf5 file""" - with h5.File(path, "r") as compas_file: - data = [ - compas_file[group][var_name][...].squeeze().flatten() for var_name in var_names - ] - if mask is not None: # apply mask if given - for i in range(len(data)): # not using list comprehension to avoid copying data - data[i] = data[i][mask] - return data - - -def generate_mock_bbh_population_file( - filename: str = "", n_systems: int = 2000, frac_bbh: float = 0.7, m1_min: float = M1_MIN, - m1_max: float = M1_MAX, m2_min: float = M2_MIN, -): - """Generate a mock BBH population file with the given number of systems and BBHs. - - Args: - filename: The filename of the mock population file. - n_systems: The number of systems in the population. - frac_bbh: The fraction of systems that are BBHs. - m1_min: The minimum mass of the primary star in solar masses. - m1_max: The maximum mass of the primary star in solar masses. - m2_min: The minimum mass of the secondary star in solar masses. - - Returns: filename - """ - if filename == "": - filename = f"bbh_mock_population.h5" - - m1, m2 = draw_samples_from_kroupa_imf( - n_samples=n_systems, Mlower=m1_min, Mupper=m1_max, m2_low=m2_min) - n_systems = len(m1) - - # draw BBH masses - mask = np.random.uniform(size=n_systems) < frac_bbh - n_bbh = np.sum(mask) - - SYS_PARAM = "BSE_System_Parameters" - DCO = "BSE_Double_Compact_Objects" - CE = "BSE_Common_Envelopes" - with h5py.File(filename, "w") as f: - f.create_group(SYS_PARAM) - f.create_group(DCO) - f.create_group(CE) - f[SYS_PARAM].create_dataset("SEED", data=np.arange(n_systems)) - f[SYS_PARAM].create_dataset("Metallicity@ZAMS(1)", data=np.random.uniform(1e-4, 1e-2, size=n_systems)) - f[SYS_PARAM].create_dataset("Mass@ZAMS(1)", data=m1) - f[SYS_PARAM].create_dataset("Mass@ZAMS(2)", data=m2) - f[CE].create_dataset("SEED", data=np.arange(n_systems)) - f[CE].create_dataset("Immediate_RLOF>CE", data=np.array([False] * n_systems)) - f[CE].create_dataset("Optimistic_CE", data=np.array([False] * n_systems)) - f[DCO].create_dataset("SEED", data=np.arange(n_bbh)) - f[DCO].create_dataset("Mass(1)", data=m1[mask]) - f[DCO].create_dataset("Mass(2)", data=m2[mask]) - f[DCO].create_dataset("Time", data=np.random.uniform(4, 13.8, size=n_bbh)) - f[DCO].create_dataset("Coalescence_Time", data=np.random.uniform(0, 14000, size=n_bbh)) - f[DCO].create_dataset("Stellar_Type(1)", data=np.array([14] * n_bbh)) - f[DCO].create_dataset("Stellar_Type(2)", data=np.array([14] * n_bbh)) - f[DCO].create_dataset("Merges_Hubble_Time", data=np.array([True] * n_bbh)) - return filename diff --git a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/binary_population.py b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/binary_population.py new file mode 100644 index 000000000..d3a0ed5b1 --- /dev/null +++ b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/binary_population.py @@ -0,0 +1,339 @@ +import numpy as np +import h5py +from .gpu_utils import xp +from typing import List, Optional + +from ..totalMassEvolvedPerZ import ( + analytical_star_forming_mass_per_binary_using_kroupa_imf, + draw_samples_from_kroupa_imf, +) +from .conversions import m1_m2_to_chirp_mass, m1_m2_to_eta +from .plotting import plot_binary_population +from .stellar_type import BH, NS, WD + +DCO_GROUPS = dict( + BBH=[BH, BH], + BNS=[NS, NS], + BWD=[WD, WD], + NSBH=[NS, BH], + WDNS=[WD, NS], + WDBH=[WD, BH], +) + +VALID_DCO_TYPES = list(DCO_GROUPS.keys()) + + +class BinaryPopulation: + """ + General DCO population class supporting BBH, BNS, NSBH. + + Loads the DCOs population from COMPAS output. + Requires the COMPAS output to contain the following datasets: + Size N (number of systems) + - BSE_System_Parameters/SEED + - BSE_System_Parameters/Metallicity@ZAMS(1) + + Size N_CE (number of CE events) <= N + - BSE_Common_Envelopes/SEED + - BSE_Common_Envelopes/Immediate_RLOF>CE + - BSE_Common_Envelopes/Optimistic_CE + + Size N_DCOs (number of N_DCOs) <= N + - BSE_Double_Compact_Objects/SEED + - BSE_Double_Compact_Objects/Mass(1) + - BSE_Double_Compact_Objects/Mass(2) + - BSE_Double_Compact_Objects/Time + - BSE_Double_Compact_Objects/Coalescence_Time + - BSE_Double_Compact_Objects/Stellar_Type(1) + - BSE_Double_Compact_Objects/Stellar_Type(2) + - BSE_Double_Compact_Objects/Merges_Hubble_Time + + """ + + def __init__( + self, + m1: np.ndarray, + m2: np.ndarray, + t_delay: np.ndarray, + z_zams: np.ndarray, + n_systems: int, + dcos_included: List[str], + m1_min: float = None, + m1_max: float = None, + m2_min: float = None, + binary_fraction: float = 0.7, + ): + # Population selection + self.dcos_included = dcos_included + if not any([x in VALID_DCO_TYPES for x in dcos_included]): + raise ValueError( + f"Invalid DCO types: {dcos_included}. " + f"Valid types are: {VALID_DCO_TYPES}" + ) + + # IMF parameters + self.m1_min = m1_min + self.m1_max = m1_max + self.m2_min = m2_min + self.binary_fraction = binary_fraction + self.mass_evolved_per_binary = analytical_star_forming_mass_per_binary_using_kroupa_imf( + m1_max=self.m1_max, + m1_min=self.m1_min, + m2_min=self.m2_min, + fbin=self.binary_fraction, + ) + + # Data arrays + self.m1 = m1 + self.m2 = m2 + self.t_delay = t_delay + self.z_zams = z_zams + self.n_systems = n_systems + + @classmethod + def from_compas_h5( + cls, + path: str, + dcos_included: List[str] = ["BBH"], + m1_min: float = None, + m1_max: float = None, + m2_min: float = None, + binary_fraction: float = 0.7, + ) -> "BinaryPopulation": + mask = cls._generate_mask(path, dcos_included) + + m1, m2, t0, tC, seeds = _load_data( + path, + "BSE_Double_Compact_Objects", + ["Mass(1)", "Mass(2)", "Time", "Coalescence_Time", "SEED"], + mask=mask, + ) + seeds = seeds.flatten() + + all_seeds, z_zams = _load_data( + path, + "BSE_System_Parameters", + ["SEED", "Metallicity@ZAMS(1)"], + ) + dco_mask = xp.in1d(all_seeds, seeds) + + return cls( + m1=m1, + m2=m2, + t_delay=t0 + tC, + z_zams=z_zams[dco_mask], + n_systems=len(all_seeds), + dcos_included=dcos_included, + m1_min=m1_min, + m1_max=m1_max, + m2_min=m2_min, + binary_fraction=binary_fraction, + ) + + @staticmethod + def _generate_mask( + path: str, + dcos_included: List[str], + ) -> xp.ndarray: + type_mask = _generate_dco_mask(path, dcos_included) + + # Load the Hubble time flag + BSE seeds + hubble_flag, dco_seeds = _load_data( + path, + "BSE_Double_Compact_Objects", + ["Merges_Hubble_Time", "SEED"], + ) + + # Hubble time filter + hubble_mask = hubble_flag.astype(bool) + + # get the flags and unique seeds from the Common Envelopes file + ce_seeds, rlof_flag, optimistic_ce = _load_data( + path, "BSE_Common_Envelopes", ["SEED", "Immediate_RLOF>CE", "Optimistic_CE"]) + dco_from_ce = xp.in1d(ce_seeds, dco_seeds) + dco_ce_seeds = ce_seeds[dco_from_ce] + del ce_seeds + + # mask out all DCOs that have RLOF after CE + rlof_flag = rlof_flag[dco_from_ce].astype(bool) + rlof_seeds = xp.unique(dco_ce_seeds[rlof_flag]) + mask_out_with_rlof_seeds = xp.logical_not(xp.in1d(dco_seeds, rlof_seeds)) + del rlof_flag, rlof_seeds + + # mask out all DCOs that have an "optimistic CE" + optimistic_ce_flag = optimistic_ce[dco_from_ce].astype(bool) + optimistic_ce_seeds = xp.unique(dco_ce_seeds[optimistic_ce_flag]) + mask_out_optimistic_ce_seeds = xp.logical_not(xp.in1d(dco_seeds, optimistic_ce_seeds)) + del optimistic_ce_flag, optimistic_ce_seeds + + lens = dict( + type=len(type_mask), + hubble=len(hubble_mask), + rlof=len(mask_out_with_rlof_seeds), + ce=len(mask_out_optimistic_ce_seeds) + ) + # assert all lens are equal + assert len(set(lens.values())) == 1, f"Length mismatch in masks: {lens}" + + return type_mask * hubble_mask * mask_out_with_rlof_seeds * mask_out_optimistic_ce_seeds + + @property + def n_dcos(self) -> int: + return len(self.m1) + + @property + def chirp_mass(self) -> np.ndarray: + if not hasattr(self, "_chirp_mass"): + self._chirp_mass = m1_m2_to_chirp_mass(self.m1, self.m2) + return self._chirp_mass + + @property + def eta(self) -> np.ndarray: + if not hasattr(self, "_eta"): + self._eta = m1_m2_to_eta(self.m1, self.m2) + return self._eta + + @property + def avg_sf_mass_needed(self) -> float: + return self.mass_evolved_per_binary * self.n_systems + + def plot(self): + arr = xp.asarray([ + self.m1, + self.m2, + self.chirp_mass, + np.log(self.z_zams), + np.log(self.t_delay), + ]).T + labels = [ + r"$m_1\ [M_{\odot}]$", + r"$m_2\ [M_{\odot}]$", + r"$\mathcal{M}_{\rm chirp}\ [M_{\odot}]$", + r"$\ln z_{\rm ZAMS}$", + r"$\ln t_{\rm delay}\ [\ln {\rm Myr}]$", + ] + return plot_binary_population(data=arr, params=labels) + + def bootstrap_population(self) -> "BinaryPopulation": + n = np.random.poisson(self.n_dcos) + idx = np.random.choice(self.n_dcos, size=n, replace=True) + return BinaryPopulation( + m1=self.m1[idx], + m2=self.m2[idx], + t_delay=self.t_delay[idx], + z_zams=self.z_zams[idx], + n_systems=self.n_systems, + dcos_included=self.dcos_included, + m1_min=self.m1_min, + m1_max=self.m1_max, + m2_min=self.m2_min, + binary_fraction=self.binary_fraction, + ) + + @property + def label(self): + return f"n{self.n_dcos}_dco_population" + + def __repr__(self): + return f"" + + def __str__(self): + return self.__repr__() + + +def _generate_dco_mask( + compas_path: str, + dcos_included: List[str] +) -> xp.ndarray: + # Load fundamental DCO variables + t1, t2 = _load_data( + compas_path, + "BSE_Double_Compact_Objects", + ["Stellar_Type(1)", "Stellar_Type(2)"], + ) + + masks = [] + for dco in dcos_included: + if dco in DCO_GROUPS: + a = [type.value for type in DCO_GROUPS[dco][0]] + b = [type.value for type in DCO_GROUPS[dco][1]] + # check if t1 in set 'a' of stellar types and t2 in set 'b' of stellar types + masks.append((np.isin(t1, a) & np.isin(t2, b)) | (np.isin(t1, b) & np.isin(t2, a))) + + if not masks: + raise ValueError("At least one DCO type must be included.") + + # Combine all masks using logical OR + type_mask = np.logical_or.reduce(masks) + return type_mask + + +def _load_data(path: str, group: str, var_names: List[str], mask: Optional[xp.ndarray] = None): + with h5py.File(path, "r") as f: + data = [f[group][v][...].squeeze().flatten() for v in var_names] + if mask is not None: + data = [d[mask] for d in data] + return data + + +# Mock generation utility +def generate_mock_population( + filename: str = "", + n_systems: int = 2000, + frac_bbh: float = 0.7, + frac_bns: float = 0.2, + frac_bhns: float = 0.1, + m1_min: float = None, + m1_max: float = None, + m2_min: float = None, +): + if filename == "": + filename = "dco_mock_population.h5" + + if m1_min is None or m1_max is None or m2_min is None: + raise ValueError("m1_min, m1_max, and m2_min must be provided to generate_mock_population") + + # sample masses and assign types + m1, m2 = draw_samples_from_kroupa_imf(n_samples=n_systems, Mlower=m1_min, Mupper=m1_max, m2_low=m2_min) + n_systems = len(m1) + n_dcos = n_systems // 2 + n_ce = n_systems * 2 + types = np.random.choice(["BBH", "BNS", "NSBH"], size=n_dcos, + p=[frac_bbh, frac_bns, frac_bhns]) + + # Define the type-to-mass mapping + type_to_pair = { + "BBH": [14, 14], + "BNS": [13, 13], + "NSBH": [13, 14] + } + + # Create a 2D array by mapping each type to its corresponding mass pair + mass_pairs = np.array([type_to_pair[t] for t in types]).T + + # create file structure + with h5py.File(filename, "w") as f: + f.create_group("BSE_System_Parameters") + f.create_group("BSE_Common_Envelopes") + f.create_group("BSE_Double_Compact_Objects") + seeds = np.arange(n_systems) + f["BSE_System_Parameters"].create_dataset("SEED", data=seeds) + f["BSE_System_Parameters"].create_dataset("Metallicity@ZAMS(1)", data=np.random.uniform(1e-4, 1e-2, n_systems)) + f["BSE_System_Parameters"].create_dataset("Mass@ZAMS(1)", data=m1) + f["BSE_System_Parameters"].create_dataset("Mass@ZAMS(2)", data=m2) + # CE + ce_seeds = np.arange(n_ce) + f["BSE_Common_Envelopes"].create_dataset("SEED", data=ce_seeds) + f["BSE_Common_Envelopes"].create_dataset("Immediate_RLOF>CE", data=np.zeros(n_ce, dtype=bool)) # no RLOF after CE + f["BSE_Common_Envelopes"].create_dataset("Optimistic_CE", data=np.zeros(n_ce, dtype=bool)) # no optimistic CE + # DCOs + dco_seeds = np.arange(n_dcos) + f["BSE_Double_Compact_Objects"].create_dataset("Stellar_Type(1)", data=mass_pairs[0, :]) + f["BSE_Double_Compact_Objects"].create_dataset("Stellar_Type(2)", data=mass_pairs[1, :]) + f["BSE_Double_Compact_Objects"].create_dataset("SEED", data=dco_seeds) + f["BSE_Double_Compact_Objects"].create_dataset("Mass(1)", data=m1[:n_dcos]) + f["BSE_Double_Compact_Objects"].create_dataset("Mass(2)", data=m2[:n_dcos]) + f["BSE_Double_Compact_Objects"].create_dataset("Time", data=np.random.uniform(4, 13.8, n_dcos)) + f["BSE_Double_Compact_Objects"].create_dataset("Coalescence_Time", data=np.random.uniform(0, 14000, n_dcos)) + f["BSE_Double_Compact_Objects"].create_dataset("Merges_Hubble_Time", data=np.ones(n_dcos, dtype=bool)) + return filename diff --git a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_matrix.py b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_matrix.py index f99305308..25f7e272b 100644 --- a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_matrix.py +++ b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_matrix.py @@ -1,10 +1,10 @@ import numpy as np import os -from typing import Dict +from typing import Dict, List import h5py as h5 from tqdm.auto import trange -from .bbh_population import BBHPopulation +from .binary_population import BinaryPopulation from .cosmological_model import CosmologicalModel from .snr_grid import SNRGrid from .gpu_utils import xp @@ -23,9 +23,10 @@ def __init__( chirp_mass_bins: np.array, redshift_bins: np.array, n_systems: int, - n_bbh: int, + n_dcos: int, outdir: str = None, sens: str = 'O1', + dcos_included: List[str] = ["BBH"], bootstrapped_rate_matrices: np.ndarray = None ): self.compas_path = compas_path @@ -36,7 +37,8 @@ def __init__( self.outdir = outdir self.bootstrapped_rate_matrices = bootstrapped_rate_matrices self.n_systems = n_systems - self.n_bbh = n_bbh + self.n_dcos = n_dcos + self.dcos_included = dcos_included self.sens = sens @property @@ -62,18 +64,19 @@ def from_compas_output( outdir: str = None, save_plots: bool = False, n_bootstrapped_matrices: int = 0, - sens: str = 'O1' + sens: str = 'O1', + dcos_included: List[str] = ["BBH"], ) -> "DetectionMatrix": - bbh_population = BBHPopulation.from_compas_h5(compas_path) + dco_population = BinaryPopulation.from_compas_h5(compas_path, dcos_included=dcos_included) cosmological_model = CosmologicalModel(**cosmological_parameters) snr_grid = SNRGrid(sensitivity=sens) - sorted_idx = xp.argsort(bbh_population.chirp_mass) + sorted_idx = xp.argsort(dco_population.chirp_mass) redshift = cosmological_model.redshift if chirp_mass_bins is None: - chirp_mass_bins = bbh_population.chirp_mass[sorted_idx] + chirp_mass_bins = dco_population.chirp_mass[sorted_idx] elif isinstance(chirp_mass_bins, int): chirp_mass_bins = xp.linspace(3, 40, chirp_mass_bins) @@ -83,7 +86,7 @@ def from_compas_output( redshift_bins = xp.linspace(0, max_detectable_redshift, redshift_bins) rate_matrix = compute_binned_detection_rates( - bbh_population, cosmological_model, snr_grid, + dco_population, cosmological_model, snr_grid, max_detectable_redshift=max_detectable_redshift, chirp_mass_bins=chirp_mass_bins, redshift_bins=redshift_bins, @@ -96,13 +99,13 @@ def from_compas_output( chirp_mass_bins=chirp_mass_bins, redshift_bins=redshift_bins, outdir=outdir, - n_systems=bbh_population.n_systems, - n_bbh=bbh_population.n_bbh, + n_systems=dco_population.n_systems, + n_dcos=dco_population.n_dcos, ) if n_bootstrapped_matrices > 0: mycls.compute_bootstrapped_rate_matrices( - bbh_population, cosmological_model, snr_grid, + dco_population, cosmological_model, snr_grid, n_bootstrapped_matrices ) @@ -110,7 +113,7 @@ def from_compas_output( mycls.plot().savefig(f"{outdir}/plot_{mycls.label}.png") cosmological_model.plot().savefig(f"{outdir}/plot_{cosmological_model.label}.png") snr_grid.plot().savefig(f"{outdir}/plot_{snr_grid.label}.png") - bbh_population.plot().savefig(f"{outdir}/plot_{bbh_population.label}.png") + dco_population.plot().savefig(f"{outdir}/plot_{dco_population.label}.png") return mycls @classmethod @@ -132,7 +135,7 @@ def to_dict(self) -> Dict: chirp_mass_bins=self.chirp_mass_bins, redshift_bins=self.redshift_bins, n_systems=self.n_systems, - n_bbh=self.n_bbh, + n_dcos=self.n_dcos, ) @property @@ -146,7 +149,7 @@ def param_str(self): def plot(self): fig = plot_detection_rate_matrix(self.rate_matrix, self.chirp_mass_bins, self.redshift_bins) - title = f"N BBH / N systems: {self.n_bbh:,}/{self.n_systems:,}" + title = f"N DCOs / N systems: {self.n_dcos:,}/{self.n_systems:,}" fig.suptitle(title) return fig @@ -191,14 +194,14 @@ def bin_data(self, mc_bins=50, z_bins=100): self.redshift_bins = z_bins def compute_bootstrapped_rate_matrices( - self, bbh_population: BBHPopulation, cosmological_model: CosmologicalModel, + self, dco_population: BinaryPopulation, cosmological_model: CosmologicalModel, snr_grid: SNRGrid, n_bootstraps=10): """Computes bootstrapped rate matrices""" self.bootstrapped_rate_matrices = np.zeros((n_bootstraps, *self.rate_matrix.shape)) for i in trange(n_bootstraps, desc="Bootstrapping rate matrices"): - boostrap_bbh = bbh_population.bootstrap_population() + boostrap_bbh = dco_population.bootstrap_population() self.bootstrapped_rate_matrices[i] = compute_binned_detection_rates( - bbh_population=boostrap_bbh, cosmological_model=cosmological_model, snr_grid=snr_grid, + dco_population=boostrap_bbh, cosmological_model=cosmological_model, snr_grid=snr_grid, chirp_mass_bins=self.chirp_mass_bins, redshift_bins=self.redshift_bins, verbose=False diff --git a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_rate_computer.py b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_rate_computer.py index 59f5ce445..0a03e25fe 100644 --- a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_rate_computer.py +++ b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/detection_rate_computer.py @@ -2,7 +2,7 @@ from scipy.interpolate import interp1d import numpy as np -from .bbh_population import BBHPopulation +from .binary_population import BinaryPopulation from .cosmological_model import CosmologicalModel from .snr_grid import SNRGrid from .gpu_utils import xp @@ -10,7 +10,7 @@ def compute_binned_detection_rates( - bbh_population: BBHPopulation, + dco_population: BinaryPopulation, cosmological_model: CosmologicalModel, snr_grid: SNRGrid, chirp_mass_bins: np.ndarray, @@ -25,7 +25,7 @@ def compute_binned_detection_rates( If the GPU is not available, this function will perform the computation on the CPU. """ - n_formed = cosmological_model.sfr / bbh_population.avg_sf_mass_needed + n_formed = cosmological_model.sfr / dco_population.avg_sf_mass_needed # Divide the star formation rate density by the representative SF mass # calculate the formation and merger rates using what we computed above @@ -38,10 +38,10 @@ def compute_binned_detection_rates( dPdlogZ=cosmological_model.dPdlogZ, metallicities=cosmological_model.metallicities, p_draw_metallicity=cosmological_model.p_draw_metallicity, - COMPAS_metallicites=bbh_population.z_zams, - COMPAS_delay_times=bbh_population.t_delay, - COMPAS_Mc=bbh_population.chirp_mass, - COMPAS_eta=bbh_population.eta, + COMPAS_metallicites=dco_population.z_zams, + COMPAS_delay_times=dco_population.t_delay, + COMPAS_Mc=dco_population.chirp_mass, + COMPAS_eta=dco_population.eta, distances=cosmological_model.distance, snr_grid_at_1Mpc=snr_grid.snr_grid_at_1Mpc, detection_probability_from_snr=snr_grid.pdetection, diff --git a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/io.py b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/io.py index 135d7239a..32115f59a 100644 --- a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/io.py +++ b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/io.py @@ -25,14 +25,14 @@ def recursively_save_dict_contents_to_group(h5file: h5py.File, group: str, dic: def encode_for_hdf5(key, item): - if isinstance(item, np.int_): - item = int(item) - elif isinstance(item, np.float_): - item = float(item) - elif isinstance(item, np.complex_): - item = complex(item) + if isinstance(item, (np.generic, int, float, complex)): + if isinstance(item, np.integer): + item = int(item) + elif isinstance(item, np.floating): + item = float(item) + elif isinstance(item, np.complexfloating): + item = complex(item) if isinstance(item, np.ndarray): - # Numpy's wide unicode strings are not supported by hdf5 if item.dtype.kind == 'U': item = np.array(item, dtype='S') if isinstance(item, (np.ndarray, int, float, complex, str, bytes)): @@ -43,7 +43,7 @@ def encode_for_hdf5(key, item): if len(item) == 0: output = item elif isinstance(item[0], (str, bytes)) or item[0] is None: - output = list() + output = [] for value in item: if isinstance(value, str): output.append(value.encode("utf-8")) @@ -68,16 +68,17 @@ def decode_from_hdf5(item): elif isinstance(item, bytes) and item == b"__none__": output = None elif isinstance(item, (bytes, bytearray)): - output = item.decode() + output = item.decode("utf-8") elif isinstance(item, np.ndarray): if item.size == 0: output = item - elif "|S" in str(item.dtype) or isinstance(item[0], bytes): - output = [it.decode() for it in item] + elif "|S" in str(item.dtype) or (item.dtype.kind == 'S') or \ + (item.size > 0 and isinstance(item.flat[0], bytes)): + output = [it.decode("utf-8") for it in item] else: output = item - elif isinstance(item, np.bool_): + elif isinstance(item, (np.bool_, bool)): output = bool(item) else: output = item - return output + return output \ No newline at end of file diff --git a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/plotting.py b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/plotting.py index e70a0bd50..86988ffcd 100644 --- a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/plotting.py +++ b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/plotting.py @@ -219,7 +219,7 @@ def plot_snr_grid( return plt.gcf() -def plot_bbh_population( +def plot_binary_population( data: np.ndarray, params: List[str] ) -> plt.Figure: n_sys = len(data) @@ -251,5 +251,5 @@ def plot_bbh_population( splines.set_visible(False) ax.spines["bottom"].set_visible(True) - fig.suptitle(f"BBH Population ({n_sys:,} BBHs)") + fig.suptitle(f"Binary Population ({n_sys:,} Binaries)") return fig diff --git a/compas_python_utils/cosmic_integration/binned_cosmic_integrator/stellar_type.py b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/stellar_type.py new file mode 100644 index 000000000..dcc7737ee --- /dev/null +++ b/compas_python_utils/cosmic_integration/binned_cosmic_integrator/stellar_type.py @@ -0,0 +1,32 @@ +from enum import Enum, auto + + +class STELLAR_TYPE(Enum): + MS_LTE_07 = 0 + MS_GT_07 = auto() + HERTZSPRUNG_GAP = auto() + FIRST_GIANT_BRANCH = auto() + CORE_HELIUM_BURNING = auto() + EARLY_ASYMPTOTIC_GIANT_BRANCH = auto() + THERMALLY_PULSING_ASYMPTOTIC_GIANT_BRANCH = auto() + NAKED_HELIUM_STAR_MS = auto() + NAKED_HELIUM_STAR_HERTZSPRUNG_GAP = auto() + NAKED_HELIUM_STAR_GIANT_BRANCH = auto() + HELIUM_WHITE_DWARF = auto() + CARBON_OXYGEN_WHITE_DWARF = auto() + OXYGEN_NEON_WHITE_DWARF = auto() + NEUTRON_STAR = auto() + BLACK_HOLE = auto() + MASSLESS_REMNANT = auto() + CHEMICALLY_HOMOGENEOUS = auto() + STAR = auto() + BINARY_STAR = auto() + NONE = auto() + +BH = [STELLAR_TYPE.BLACK_HOLE] +NS = [STELLAR_TYPE.NEUTRON_STAR] +WD = [STELLAR_TYPE.HELIUM_WHITE_DWARF, + STELLAR_TYPE.CARBON_OXYGEN_WHITE_DWARF, + STELLAR_TYPE.HELIUM_WHITE_DWARF, + STELLAR_TYPE.OXYGEN_NEON_WHITE_DWARF] + diff --git a/compas_python_utils/cosmic_integration/totalMassEvolvedPerZ.py b/compas_python_utils/cosmic_integration/totalMassEvolvedPerZ.py index 8d1317d9c..e895b97f6 100644 --- a/compas_python_utils/cosmic_integration/totalMassEvolvedPerZ.py +++ b/compas_python_utils/cosmic_integration/totalMassEvolvedPerZ.py @@ -52,58 +52,86 @@ def IMF(m, m1=0.01, m2=0.08, m3=0.5, m4=200.0, a12=0.3, a23=1.3, a34=2.3): -def get_COMPAS_fraction(m1_low, m1_upp, m2_low, f_bin, mass_ratio_pdf_function=lambda q: 1, - m1=0.01, m2=0.08, m3=0.5, m4=200.0, a12=0.3, a23=1.3, a34=2.3): - """Calculate the fraction of mass in a COMPAS population relative to the total Universal population. This - can be used to normalise the rates of objects from COMPAS simulations. +def get_COMPAS_fraction(m1_low, m1_upp, m2_low, f_bin=None, + mass_ratio_pdf_function=lambda q: 1, + m1=0.01, m2=0.08, m3=0.5, m4=200.0, + a12=0.3, a23=1.3, a34=2.3): + """ + Calculate the fraction of mass in a COMPAS population relative to the total Universal population. + Can be used to normalise the rates of objects from COMPAS simulations. Parameters ---------- - m1_low : `float` - Lower limit on the sampled primary mass - m1_upp : `float` - Upper limit on the sampled primary mass - m2_low : `float` - Lower limit on the sampled secondary mass - f_bin : `float` - Binary fraction - mass_ratio_pdf_function : `function`, optional - Function to calculate the mass ratio PDF, by default a uniform mass ratio distribution - mi, aij : `float` - Settings for the IMF choice, see `IMF` for details, by default follows Kroupa (2001) - - Returns - ------- - fraction - The fraction of mass in a COMPAS population relative to the total Universal population - """ - # first, for normalisation purposes, we can find the integral with no COMPAS cuts - def full_integral(mass, m1, m2, m3, m4, a12, a23, a34): - primary_mass = IMF(mass, m1, m2, m3, m4, a12, a23, a34) * mass - - # find the expected companion mass given the mass ratio pdf function - expected_secondary_mass = quad(lambda q: q * mass_ratio_pdf_function(q), 0, 1)[0] * primary_mass - - single_stars = (1 - f_bin) * primary_mass - binary_stars = f_bin * (primary_mass + expected_secondary_mass) - return single_stars + binary_stars - full_mass = quad(full_integral, m1, m4, args=(m1, m2, m3, m4, a12, a23, a34))[0] + m1_low, m1_upp : float + Primary mass cuts in COMPAS simulation + m2_low : float + Secondary mass cutoff + f_bin : float or None + Binary fraction. If None, use a stepwise mass-dependent binary fraction. + mass_ratio_pdf_function : function + PDF of mass ratio q + mi, aij : float + IMF breakpoints and slopes + """ + fbinary_bin_edges = [m1, 0.08, 0.5, 1, 10, m4] - # now we do a similar integral but for the COMPAS regime - def compas_integral(mass, m2_low, f_bin, m1, m2, m3, m4, a12, a23, a34): - # define the primary mass in the same way - primary_mass = IMF(mass, m1, m2, m3, m4, a12, a23, a34) * mass - - # find the fraction that are below the m2 mass cut - f_below_m2low = quad(mass_ratio_pdf_function, 0, m2_low / mass)[0] - - # expectation value of the secondary mass given the m2 cut and mass ratio pdf function - expected_secondary_mass = quad(lambda q: q * mass_ratio_pdf_function(q), m2_low / mass, 1)[0] * primary_mass - - # return total mass of binary stars that have m2 above the cut - return f_bin * (1 - f_below_m2low) * (primary_mass + expected_secondary_mass) - compas_mass = quad(compas_integral, m1_low, m1_upp, args=(m2_low, f_bin, m1, m2, m3, m4, a12, a23, a34))[0] - return compas_mass / full_mass + def get_binary_fraction(mass): + binaryFractions = [0.1, 0.225, 0.5, 0.8, 1.0] + for i in range(len(fbinary_bin_edges) - 1): + if mass < fbinary_bin_edges[0]: + # Mass below lowest binary fraction bin edge (shouldnt happen) + return binaryFractions[0] + if fbinary_bin_edges[i] <= mass < fbinary_bin_edges[i + 1]: + return binaryFractions[i] + if mass >= fbinary_bin_edges[-1]: + # Mass above highest binary fraction bin edge (shouldnt happen) + return binaryFractions[-1] + + def integrand_full(mass, f_bin): + local_f_bin = get_binary_fraction(mass) if f_bin is None else f_bin + expected_q = quad(lambda q: q * mass_ratio_pdf_function(q), 0, 1)[0] + # mass of single stars = (1 - f_bin) * m1 + # mass of binaries = f_bin * (1 + ) * m1 + expected_mass_all_stellar_sys =(1 + local_f_bin * expected_q) * mass * IMF(mass, m1, m2, m3, m4, a12, a23, a34) + return expected_mass_all_stellar_sys + + def integrand_compas(mass, f_bin): + local_f_bin = get_binary_fraction(mass) if f_bin is None else f_bin + # Only binaries contribute in COMPAS population + # Integrand is (1 + q) * f_bin * m1 * P(m1) * P(q), + q_min = m2_low / mass + if q_min >= 1: + return 0 # No valid secondaries + # Integrate (1 + q)P(q) dq over q from q_min to 1, + # we get p(q)dq: + p_qdq = quad(mass_ratio_pdf_function, q_min, 1)[0] + # and q P(q) dq (= expected_q) + expected_q = quad(lambda q: q * mass_ratio_pdf_function(q), q_min, 1)[0] + + expected_mass_compas_binaries = (p_qdq + expected_q) * local_f_bin * mass * IMF(mass, m1, m2, m3, m4, a12, a23, a34) + return expected_mass_compas_binaries + + + # split integral at binary fraction steps if f_bin is None (i.e. variable and like a step function) + def split_integral(func, a, b, f_bin): + total = 0 + for edge_start, edge_end in zip(fbinary_bin_edges[:-1], fbinary_bin_edges[1:]): + left = max(a, edge_start) + right = min(b, edge_end) + if left < right: + result, _ = quad(func, left, right, args=(f_bin,)) + total += result + return total + + if f_bin is None: + full_mass = split_integral(integrand_full, m1, m4, f_bin) + compas_mass = split_integral(integrand_compas, m1_low, m1_upp, f_bin) + else: + full_mass = quad(integrand_full, m1, m4, args=(f_bin,))[0] + compas_mass = quad(integrand_compas, m1_low, m1_upp, args=(f_bin,))[0] + + fraction = compas_mass / full_mass + return fraction def retrieveMassEvolvedPerZ(path): @@ -125,17 +153,18 @@ def totalMassEvolvedPerZ(path, Mlower, Mupper, m2_low, binaryFraction, mass_rati """ Calculate the total mass evolved per metallicity as a function of redshift in a COMPAS simulation. """ - # calculate the fraction of mass in the COMPAS simulation vs. the real population without sample cuts fraction = get_COMPAS_fraction(m1_low=Mlower, m1_upp=Mupper, m2_low=m2_low, f_bin=binaryFraction, mass_ratio_pdf_function=mass_ratio_pdf_function, m1=m1, m2=m2, m3=m3, m4=m4, a12=a12, a23=a23, a34=a34) + multiplicationFactor = 1 / fraction + # Warning: This is slow and error prone! esp if you sample metallicities smoothly # get the mass evolved for each metallicity bin and convert to a total mass using the fraction MassEvolvedPerZ = retrieveMassEvolvedPerZ(path) - totalMassEvolvedPerMetallicity = MassEvolvedPerZ / fraction + totalMassEvolvedPerMetallicity = MassEvolvedPerZ / fraction return multiplicationFactor, totalMassEvolvedPerMetallicity @@ -147,7 +176,10 @@ def star_forming_mass_per_binary( """ Calculate the total mass of stars formed per binary star formed within the COMPAS simulation. """ - multiplicationFactor, _ = totalMassEvolvedPerZ(**locals()) + fraction = get_COMPAS_fraction(m1_low=Mlower,m1_upp=Mupper,m2_low=m2_low, + f_bin=binaryFraction,mass_ratio_pdf_function=mass_ratio_pdf_function, + m1=m1, m2=m2, m3=m3, m4=m4, + a12=a12, a23=a23, a34=a34) # get the total mass in COMPAS and number of binaries with h5.File(path, 'r') as f: @@ -157,7 +189,7 @@ def star_forming_mass_per_binary( n_binaries = len(m1s) total_star_forming_mass_in_COMPAS = sum(m1s) + sum(m2s) - total_star_forming_mass = total_star_forming_mass_in_COMPAS * multiplicationFactor + total_star_forming_mass = total_star_forming_mass_in_COMPAS / fraction return total_star_forming_mass / n_binaries @@ -191,28 +223,125 @@ def draw_samples_from_kroupa_imf( return m1_samples[mask] , m2_samples[mask] + +################################################### +# Analytical calculation of star forming mass per binary +################################################### def analytical_star_forming_mass_per_binary_using_kroupa_imf( - m1_min, m1_max, m2_min, fbin=1., imf_mass_bounds=[0.01,0.08,0.5,200] -): + m1_min, m1_max, m2_min, fbin=1.0, imf_mass_bounds=(0.01, 0.08, 0.5, 200.0)): """ - Analytical computation of the mass of stars formed per binary star formed within the - [m1 min, m1 max] and [m2 min, ..] rage, - using the Kroupa IMF: + Takes: + m1_min, m1_max, m2_min: COMPAS mass ranges [Msun] + fbin: binary fraction (if None, use piecewise constant fbin(m1)) + imf_mass_bounds: Kroupa IMF mass bounds [Msun] + Computes: + N_bin_in_COMPAS + average_stellar_mass_sys = M_sys,Univ / N_sys,Univ (blue) + M_sf_Univ_per_N_binary_COMPAS = average_stellar_mass_sys / N_bin_in_COMPAS - p(M) \propto M^-0.3 for M between m1 and m2 - p(M) \propto M^-1.3 for M between m2 and m3; - p(M) = alpha * M^-2.3 for M between m3 and m4; - - @Ilya Mandel's derivation + Assumes: + Kroupa IMF: P(m1) = alpha * C_cont,i * m1^{gamma_i} on IMF segment i + Piecewise constant on binary-fraction: f_bin(m1) with bins j + Flat mass ratio: P(q)=U(0,1) => P(m2>m2_min | m1) = 1 - m2_min/m1 (for m1 >= m2_min) """ + # ------------------------- + # Kroupa IMF + # ------------------------- m1, m2, m3, m4 = imf_mass_bounds - if m1_min < m3: - raise ValueError(f"This analytical derivation requires IMF break m3 < m1_min ({m3} !< {m1_min})") - alpha = (-(m4**(-1.3)-m3**(-1.3))/1.3 - (m3**(-0.3)-m2**(-0.3))/(m3*0.3) + (m2**0.7-m1**0.7)/(m2*m3*0.7))**(-1) - # average mass of stars (average mass of all binaries is a factor of 1.5 larger) - m_avg = alpha * (-(m4**(-0.3)-m3**(-0.3))/0.3 + (m3**0.7-m2**0.7)/(m3*0.7) + (m2**1.7-m1**1.7)/(m2*m3*1.7)) - # fraction of binaries that COMPAS simulates - fint = -alpha / 1.3 * (m1_max ** (-1.3) - m1_min ** (-1.3)) + alpha * m2_min / 2.3 * (m1_max ** (-2.3) - m1_min ** (-2.3)) - # mass represented by each binary simulated by COMPAS - m_rep = (1/fint) * m_avg * (1.5 + (1-fbin)/fbin) - return m_rep \ No newline at end of file + continuity_constants = [1.0 / (m2 * m3), 1.0 / m3, 1.0] + IMF_powers = [-0.3, -1.3, -2.3] + + if m1_min > m1_max: + raise ValueError(f"Require m1_min <= m1_max, got {m1_min} > {m1_max}.") + if m1_min < m1: + raise ValueError(f"Require m1_min >= {m1} (Universe lower IMF bound).") + if m1_max > m4: + raise ValueError(f"Require m1_max <= {m4} (Universe upper IMF bound).") + + # Normalization alpha over full IMF range [m1, m4] + alpha = ( + - (m4 ** (-1.3) - m3 ** (-1.3)) / 1.3 + - (m3 ** (-0.3) - m2 ** (-0.3)) / (m3 * 0.3) + + (m2 ** (0.7) - m1 ** (0.7)) / (m2 * m3 * 0.7) + ) ** (-1) + + # ------------------------- + # Binary-fraction bins + # ------------------------- + fbinary_bin_edges = [m1, 0.08, 0.5, 1.0, 10.0, m4] + if fbin is None: + binaryFractions = [0.1, 0.225, 0.5, 0.8, 1.0] + else: + binaryFractions = [float(fbin)] * (len(fbinary_bin_edges) - 1) + + # ------------------------- + # Helpers: overlaps and power integrals + # ------------------------- + def overlap(lo1, hi1, lo2, hi2): + lo = max(lo1, lo2) + hi = min(hi1, hi2) + return lo, hi + + def int_power(lo, hi, power): + """∫_lo^hi m^{power} dm, for power != -1.""" + if hi <= lo: + return 0.0 + return (hi ** (power + 1) - lo ** (power + 1)) / (power + 1) + + # ------------------------- + # Average_stellar_mass_sys = ∫_Univ (1+0.5 fbin) m1 P(m1) dm1 + # ------------------------- + av_Mstar_integral_sum = 0.0 + for i in range(len(IMF_powers)): # IMF segment index i + gamma_i = IMF_powers[i] + C_cont_i = continuity_constants[i] + imf_lo, imf_hi = imf_mass_bounds[i], imf_mass_bounds[i + 1] + + for j in range(len(binaryFractions)): # fbin bin index j + fbin_j = binaryFractions[j] + fb_lo, fb_hi = fbinary_bin_edges[j], fbinary_bin_edges[j + 1] + + A, B = overlap(imf_lo, imf_hi, fb_lo, fb_hi) + if B <= A: + continue + + # integrand: (1 + 0.5 fbin_j) * m1 * (alpha*C_cont_i*m1^{gamma_i}) + # => alpha * (1+0.5 fbin_j) * C_cont_i * ∫ m1^{gamma_i+1} dm1 + av_Mstar_integral_sum += (1.0 + 0.5 * fbin_j) * C_cont_i * int_power(A, B, gamma_i + 1) + + average_stellar_mass_sys = alpha * av_Mstar_integral_sum + + # ------------------------- + # N_bin_in_COMPAS = ∫_{m1_min}^{m1_max} P(m1) fbin(m1) (1 - m2_min/m1) dm1 + # ------------------------- + N_bin_compas_sum = 0.0 + for i in range(len(IMF_powers)): # IMF segment index i + gamma_i = IMF_powers[i] + C_cont_i = continuity_constants[i] + imf_lo, imf_hi = imf_mass_bounds[i], imf_mass_bounds[i + 1] + + for j in range(len(binaryFractions)): # fbin bin index j + fbin_j = binaryFractions[j] + fb_lo, fb_hi = fbinary_bin_edges[j], fbinary_bin_edges[j + 1] + + # overlap additionally with COMPAS m1-range + A, B = overlap(imf_lo, imf_hi, fb_lo, fb_hi) + A, B = overlap(A, B, m1_min, m1_max) + if B <= A: + continue + + # integrand: alpha*C_cont_i*m^{gamma_i} * fbin_j * (1 - m2_min/m) + # => alpha*fbin_j*C_cont_i * ∫ (m^{gamma_i} - m2_min*m^{gamma_i-1}) dm + term_main = int_power(A, B, gamma_i) # ∫ m^{gamma_i} dm + term_m2 = int_power(A, B, gamma_i - 1) # ∫ m^{gamma_i-1} dm + N_bin_compas_sum += fbin_j * C_cont_i * (term_main - m2_min * term_m2) + + N_bin_in_COMPAS = alpha * N_bin_compas_sum + + if N_bin_in_COMPAS <= 0.0: + raise ValueError("Computed N_bin_in_COMPAS <= 0; check bounds and m2_min.") + + M_sf_Univ_per_N_binary_COMPAS = average_stellar_mass_sys / N_bin_in_COMPAS + + return M_sf_Univ_per_N_binary_COMPAS + diff --git a/compas_python_utils/preprocessing/compasConfigDefault.yaml b/compas_python_utils/preprocessing/compasConfigDefault.yaml index 48ab8e151..89ab35b98 100644 --- a/compas_python_utils/preprocessing/compasConfigDefault.yaml +++ b/compas_python_utils/preprocessing/compasConfigDefault.yaml @@ -1,5 +1,5 @@ ##~!!~## COMPAS option values -##~!!~## File Created Tue Apr 1 17:29:43 2025 by COMPAS v03.17.00 +##~!!~## File Created Tue Dec 9 15:41:35 2025 by COMPAS v03.27.02 ##~!!~## ##~!!~## The default COMPAS YAML file (``compasConfigDefault.yaml``), as distributed, has ##~!!~## all COMPAS option entries commented so that the COMPAS default value for the @@ -25,12 +25,11 @@ booleanChoices: ### STELLAR PROPERTIES # --check-photon-tiring-limit: False # Default: False -# --use-mass-loss: True # Default: True # --enable-rotationally-enhanced-mass-loss: False # Default: False -# --enhance-CHE-lifetimes-luminosities: False # Default: False +# --enhance-CHE-lifetimes-luminosities: True # Default: True # --expel-convective-envelope-above-luminosity-threshold: False # Default: False # --natal-kick-for-PPISN: False # Default: False -# --scale-CHE-mass-loss-with-surface-helium-abundance: False # Default: False +# --scale-CHE-mass-loss-with-surface-helium-abundance: True # Default: True ### BINARY PROPERTIES # --allow-touching-at-birth: False # Default: False # record binaries that have stars touching at birth in output files @@ -48,9 +47,9 @@ booleanChoices: # --common-envelope-allow-immediate-RLOF-post-CE-survive: False # Default: False # --common-envelope-allow-main-sequence-survive: True # Default: True # Allow main sequence stars to survive CE # --common-envelope-allow-radiative-envelope-survive: False # Default: False -# --common-envelope-lambda-nanjing-enhanced: False # Default: False -# --common-envelope-lambda-nanjing-interpolate-in-mass: False # Default: False -# --common-envelope-lambda-nanjing-interpolate-in-metallicity: False # Default: False +# --common-envelope-lambda-nanjing-enhanced: True # Default: True +# --common-envelope-lambda-nanjing-interpolate-in-mass: True # Default: True +# --common-envelope-lambda-nanjing-interpolate-in-metallicity: True # Default: True # --common-envelope-lambda-nanjing-use-rejuvenated-mass: False # Default: False # --revised-energy-formalism-nandez-ivanova: False # Default: False @@ -58,6 +57,7 @@ booleanChoices: # --allow-non-stripped-ECSN: False # Default: False # --pair-instability-supernovae: True # Default: True # --pulsational-pair-instability: True # Default: True +# --USSN-kicks-override-mandel-muller: False # Default: False ### PULSAR PARAMETERS # --evolve-pulsars: False # Default: False @@ -71,11 +71,12 @@ numericalChoices: ### LOGISTICS # --debug-level: 0 # Default: 0 # --logfile-common-envelopes-record-types: -1 # Default: -1 -# --logfile-detailed-output-record-types: -1 # Default: -1 +# --logfile-detailed-output-record-types: 25 # Default: 25 # --logfile-double-compact-objects-record-types: -1 # Default: -1 -# --logfile-pulsar-evolution-record-types: -1 # Default: -1 +# --logfile-pulsar-evolution-record-types: 4 # Default: 4 # --logfile-rlof-parameters-record-types: -1 # Default: -1 # --logfile-supernovae-record-types: -1 # Default: -1 +# --logfile-system-snapshot-log-record-types: -1 # Default: -1 # --logfile-system-parameters-record-types: -1 # Default: -1 # --grid-lines-to-process: 9223372036854775807 # Default: 9223372036854775807 # --grid-start-line: 0 # Default: 0 @@ -83,18 +84,21 @@ numericalChoices: # --hdf5-buffer-size: 1 # Default: 1 # --log-level: 0 # Default: 0 # --mass-change-fraction: 0.001000 # Default: 0.001000 # approximate desired fractional changes in stellar mass per timestep -# --maximum-evolution-time: 13700.00 # Default: 13700.00 # maximum physical time a system can be evolved [Myr] +# --maximum-evolution-time: 14021.28 # Default: 14021.28 # maximum physical time a system can be evolved [Myr] # --maximum-number-timestep-iterations: 99999 # Default: 99999 # --number-of-systems: 10 # Default: 10 # number of systems per batch # --radial-change-fraction: 0.100000 # Default: 0.100000 # approximate desired fractional changes in stellar radius per timestep +# --system-snapshot-age-thresholds: '{ }' # Default: '' # age thresholds for the system snapshot log +# --system-snapshot-time-thresholds: '{ }' # Default: '' # time thresholds for the system snapshot log # --timestep-multiplier: 1.000000 # Default: 1.000000 # optional multiplier relative to default time step duration +# --timestep-multipliers: '{ }' # Default: '' # optional phase-dependent multipliers relative to default time step duration ### STELLAR PROPERTIES # --cool-wind-mass-loss-multiplier: 1.000000 # Default: 1.000000 # --initial-mass: 5.000000 # Default: 5.000000 # initial mass for SSE -# --initial-mass-min: 5.000000 # Default: 5.000000 # use 5.0 for DCOs [Msol] -# --initial-mass-max: 150.00 # Default: 150.00 # stellar tracks extrapolated above 50 Msol (Hurley+2000) [Msol] -# --initial-mass-power: 0.000000 # Default: 0.000000 +# --initial-mass-function-min: 5.000000 # Default: 5.000000 # use 5.0 for DCOs [Msol] +# --initial-mass-function-max: 150.00 # Default: 150.00 # stellar tracks extrapolated above 50 Msol (Hurley+2000) [Msol] +# --initial-mass-function-power: 0.000000 # Default: 0.000000 # --luminosity-to-mass-threshold: 4.200000 # Default: 4.200000 # --metallicity: 0.014200 # Default: 0.014200 # metallicity for both SSE and BSE - Solar metallicity Asplund+2010 # --metallicity-min: 0.000100 # Default: 0.000100 @@ -117,7 +121,7 @@ numericalChoices: # --mass-ratio: 1.000000 # Default: 1.000000 # --mass-ratio-min: 0.010000 # Default: 0.010000 # --mass-ratio-max: 1.000000 # Default: 1.000000 -# --minimum-secondary-mass: 0.100000 # Default: 0.100000 # Brown dwarf limit [Msol] +# --minimum-sampled-secondary-mass: 0.100000 # Default: 0.100000 # Brown dwarf limit [Msol] # --orbital-period: 0.100000 # Default: 0.100000 # orbital period for BSE # --orbital-period-min: 1.100000 # Default: 1.100000 # [days] # --orbital-period-max: 1000.00 # Default: 1000.00 # [days] @@ -146,8 +150,8 @@ numericalChoices: # --critical-mass-ratio-white-dwarf-non-degenerate-accretor: 0.000000 # Default: 0.000000 # --mass-transfer-fa: 0.500000 # Default: 0.500000 # Only if using mass-transfer-accretion-efficiency-prescription = 'FIXED' # --mass-transfer-jloss: 1.000000 # Default: 1.000000 # Only if using mass-transfer-angular-momentum-loss-prescription = 'FIXED' -# --mass-transfer-jloss-macleod-linear-fraction-degen: 0.500000 # Default: 0.500000 -# --mass-transfer-jloss-macleod-linear-fraction-non-degen: 0.500000 # Default: 0.500000 +# --mass-transfer-jloss-linear-fraction-degen: 0.500000 # Default: 0.500000 +# --mass-transfer-jloss-linear-fraction-non-degen: 0.500000 # Default: 0.500000 # --mass-transfer-thermal-limit-C: 10.000000 # Default: 10.000000 # --zeta-adiabatic-arbitrary: 10000.00 # Default: 10000.00 # --zeta-main-sequence: 2.000000 # Default: 2.000000 @@ -162,6 +166,7 @@ numericalChoices: # --common-envelope-mass-accretion-max: 0.100000 # Default: 0.100000 # For 'MACLEOD+2014' [Msol] # --common-envelope-mass-accretion-min: 0.040000 # Default: 0.040000 # For 'MACLEOD+2014' [Msol] # --common-envelope-recombination-energy-density: 15000000000000.00 # Default: 15000000000000.00 +# --common-envelope-second-stage-beta: 0.000000 # Default: 0.000000 # --common-envelope-slope-kruckow: -0.833333 # Default: -0.833333 # --maximum-mass-donor-nandez-ivanova: 2.000000 # Default: 2.000000 @@ -171,8 +176,8 @@ numericalChoices: # --fryer-22-fmix: 0.500000 # Default: 0.500000 # parameter describing mixing growth time when using the 'FRYER2022' remnant mass prescription # --fryer-22-mcrit: 5.750000 # Default: 5.750000 # critical mass for BH formation when using the 'FRYER2022' remnant mass prescription # --kick-direction-power: 0.000000 # Default: 0.000000 -# --kick-magnitude-sigma-CCSN-NS: 265.00 # Default: 265.00 # [km/s] -# --kick-magnitude-sigma-CCSN-BH: 265.00 # Default: 265.00 # [km/s] +# --kick-magnitude-sigma-CCSN-NS: 217.00 # Default: 217.00 # [km/s] +# --kick-magnitude-sigma-CCSN-BH: 217.00 # Default: 217.00 # [km/s] # --kick-magnitude-max: -1.000000 # Default: -1.000000 # --kick-magnitude-random: 0.000000 # Default: 0.000000 # (SSE) used to draw the kick magnitude for the star should it undergo a supernova event # --kick-magnitude: 0.000000 # Default: 0.000000 # (SSE) (drawn) kick magnitude for the star should it undergo a supernova event [km/s] @@ -193,7 +198,8 @@ numericalChoices: # --mcbur1: 1.600000 # Default: 1.600000 # --muller-mandel-kick-multiplier-BH: 200.00 # Default: 200.00 # scaling prefactor for BH kicks when using the 'MULLERMANDEL' kick magnitude distribution # --muller-mandel-kick-multiplier-NS: 520.00 # Default: 520.00 # scaling prefactor for NS kicks when using the 'MULLERMANDEL' kick magnitude distribution -# --muller-mandel-sigma-kick: 0.300000 # Default: 0.300000 # kick scatter when using the 'MULLERMANDEL' kick magnitude distribution +# --muller-mandel-sigma-kick-BH: 0.300000 # Default: 0.300000 # BH kick scatter when using the 'MULLERMANDEL' kick magnitude distribution +# --muller-mandel-sigma-kick-NS: 0.300000 # Default: 0.300000 # NS kick scatter when using the 'MULLERMANDEL' kick magnitude distribution # --neutrino-mass-loss-BH-formation-value: 0.100000 # Default: 0.100000 # --pisn-lower-limit: 60.000000 # Default: 60.000000 # Minimum core mass for PISN [Msol] # --pisn-upper-limit: 135.00 # Default: 135.00 # Maximum core mass for PISN [Msol] @@ -238,8 +244,8 @@ stringChoices: # --envelope-state-prescription: 'LEGACY' # Default: 'LEGACY' # Options: ['CONVECTIVE_MASS_FRACTION','FIXED_TEMPERATURE','HURLEY','LEGACY'] # --initial-mass-function: 'KROUPA' # Default: 'KROUPA' # Options: ['KROUPA','UNIFORM','POWERLAW','SALPETER'] # --LBV-mass-loss-prescription: 'HURLEY_ADD' # Default: 'HURLEY_ADD' # Options: ['BELCZYNSKI','HURLEY','HURLEY_ADD','ZERO'] -# --main-sequence-core-mass-prescription: 'MANDEL' # Default: 'MANDEL' # Options: ['BRCEK','MANDEL','ZERO'] -# --mass-loss-prescription: 'MERRITT2024' # Default: 'MERRITT2024' # Options: ['MERRITT2024','BELCZYNSKI2010','HURLEY','ZERO'] +# --main-sequence-core-mass-prescription: 'MANDEL' # Default: 'MANDEL' # Options: ['BRCEK','MANDEL','HURLEY'] +# --mass-loss-prescription: 'MERRITT2025' # Default: 'MERRITT2025' # Options: ['MERRITT2025','BELCZYNSKI2010','HURLEY','ZERO'] # --OB-mass-loss-prescription: 'VINK2021' # Default: 'VINK2021' # Options: ['KRTICKA2018','BJORKLUND2022','VINK2021','VINK2001','ZERO'] # --RSG-mass-loss-prescription: 'DECIN2023' # Default: 'DECIN2023' # Options: ['NJ90','KEE2021','YANG2023','DECIN2023','BEASOR2020','VINKSABHAHIT2023','ZERO'] # --VMS-mass-loss-prescription: 'SABHAHIT2023' # Default: 'SABHAHIT2023' # Options: ['SABHAHIT2023','BESTENLEHNER2020','VINK2011','ZERO'] @@ -258,8 +264,8 @@ stringChoices: # --case-BB-stability-prescription: 'ALWAYS_STABLE' # Default: 'ALWAYS_STABLE' # Options: ['ALWAYS_UNSTABLE','TREAT_AS_OTHER_MT','ALWAYS_STABLE_ONTO_NSBH','ALWAYS_STABLE'] # --critical-mass-ratio-prescription: 'NONE' # Default: 'NONE' # Options: ['HURLEY_HJELLMING_WEBBINK','GE','GE_IC','CLAEYS','NONE'] # --stellar-zeta-prescription: 'SOBERMAN' # Default: 'SOBERMAN' # Options: ['ARBITRARY','HURLEY','SOBERMAN'] -# --mass-transfer-angular-momentum-loss-prescription: 'ISOTROPIC' # Default: 'ISOTROPIC' # Options: ['ARBITRARY','MACLEOD_LINEAR','CIRCUMBINARY','ISOTROPIC','JEANS'] -# --mass-transfer-accretion-efficiency-prescription: 'THERMAL' # Default: 'THERMAL' # Options: ['FIXED','THERMAL'] +# --mass-transfer-angular-momentum-loss-prescription: 'ISOTROPIC' # Default: 'ISOTROPIC' # Options: ['ARBITRARY','KLENCKI_LINEAR','MACLEOD_LINEAR','CIRCUMBINARY','ISOTROPIC','JEANS'] +# --mass-transfer-accretion-efficiency-prescription: 'THERMAL' # Default: 'THERMAL' # Options: ['HAMSTARS','FIXED','THERMAL'] # --mass-transfer-rejuvenation-prescription: 'STARTRACK' # Default: 'STARTRACK' # Options: ['STARTRACK','HURLEY'] # --mass-transfer-thermal-limit-accretor-multiplier: 'CFACTOR' # Default: 'CFACTOR' # Options: ['ROCHELOBE','CFACTOR'] # --response-to-spin-up: 'TRANSFER_TO_ORBIT' # Default: 'TRANSFER_TO_ORBIT' # Options: ['NO_LIMIT','KEPLERIAN_LIMIT','TRANSFER_TO_ORBIT'] @@ -268,15 +274,18 @@ stringChoices: # --common-envelope-formalism: 'ENERGY' # Default: 'ENERGY' # Options: ['TWO_STAGE','ENERGY'] # --common-envelope-lambda-prescription: 'LAMBDA_NANJING' # Default: 'LAMBDA_NANJING' # Options: ['LAMBDA_DEWI','LAMBDA_KRUCKOW','LAMBDA_NANJING','LAMBDA_LOVERIDGE','LAMBDA_FIXED'] # Xu & Li 2010 # --common-envelope-mass-accretion-prescription: 'ZERO' # Default: 'ZERO' # Options: ['CHEVALIER','MACLEOD','UNIFORM','CONSTANT','ZERO'] +# --common-envelope-second-stage-gamma-prescription: 'ISOTROPIC' # Default: 'ISOTROPIC' # Options: ['ARBITRARY','KLENCKI_LINEAR','MACLEOD_LINEAR','CIRCUMBINARY','ISOTROPIC','JEANS'] ### TIDES -# --tides-prescription: 'NONE' # Default: 'NONE' # Options: ['KAPIL2024','PERFECT','NONE'] +# --tides-prescription: 'NONE' # Default: 'NONE' # Options: ['KAPIL2025','PERFECT','NONE'] ### SUPERNOVAE, KICKS AND REMNANTS # --black-hole-kicks-mode: 'FALLBACK' # Default: 'FALLBACK' # Options: ['FALLBACK','ZERO','REDUCED','FULL'] # --fryer-supernova-engine: 'DELAYED' # Default: 'DELAYED' # Options: ['DELAYED','RAPID'] -# --kick-magnitude-distribution: 'MULLERMANDEL' # Default: 'MULLERMANDEL' # Options: ['MULLERMANDEL','MULLER2016MAXWELLIAN','MULLER2016','BRAYELDRIDGE','MAXWELLIAN','FLAT','FIXED','ZERO'] +# --kick-magnitude-distribution: 'MULLERMANDEL' # Default: 'MULLERMANDEL' # Options: ['LOGNORMAL','MULLERMANDEL','MULLER2016MAXWELLIAN','MULLER2016','BRAYELDRIDGE','MAXWELLIAN','FLAT','FIXED','ZERO'] # --kick-direction-distribution: 'ISOTROPIC' # Default: 'ISOTROPIC' # Options: ['POLES','WEDGE','POWERLAW','PERPENDICULAR','INPLANE','ISOTROPIC'] +# --maltsev-fallback: 0.500000 # Default: 0.500000 +# --maltsev-mode: 'BALANCED' # Default: 'BALANCED' # Options: ['PESSIMISTIC','BALANCED','OPTIMISTIC'] # --neutron-star-accretion-in-ce: 'ZERO' # Default: 'ZERO' # Options: ['DISK','SURFACE','ZERO'] # --neutron-star-equation-of-state: 'SSE' # Default: 'SSE' # Options: ['ARP3','SSE'] # --neutrino-mass-loss-BH-formation: 'FIXED_MASS' # Default: 'FIXED_MASS' # Options: ['FIXED_MASS','FIXED_FRACTION'] @@ -295,6 +304,7 @@ stringChoices: # --logfile-rlof-parameters: 'BSE_RLOF' # Default: 'BSE_RLOF' # --logfile-supernovae: 'BSE_Supernovae' # Default: 'BSE_Supernovae' # --logfile-switch-log: 'BSE_Switch_Log' # Default: 'BSE_Switch_Log' +# --logfile-system-snapshot-log: 'BSE_System_Snapshot_Log' # Default: 'BSE_System_Snapshot_Log' # --logfile-system-parameters: 'BSE_System_Parameters' # Default: 'BSE_System_Parameters' # --output-path: '.' # Default: '.' diff --git a/online-docs/COMPAS-2021methodsPaper.bib b/online-docs/COMPAS-2021methodsPaper.bib index be6494734..aa9f03e8d 100644 --- a/online-docs/COMPAS-2021methodsPaper.bib +++ b/online-docs/COMPAS-2021methodsPaper.bib @@ -1,25 +1,15 @@ -@ This formats the citation for the COMPAS Methods Paper in our preferred style -@ The following PREAMBLE entry must appear at the top of the bibtex file - -@PREAMBLE{ {\providecommand{\noopsort}[1]{}} } - -@ARTICLE{2022ApJS..258...34R, - author = {{Riley}, Jeff and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold and {Team Compas}}, - title = "{Rapid Stellar and Binary Population Synthesis with COMPAS}", - journal = {\apjs}, - keywords = {1622, 154, 1108, 162, Astrophysics - Instrumentation and Methods for Astrophysics, Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Solar and Stellar Astrophysics}, - year = 2022, - month = feb, - volume = {258}, - number = {2}, - eid = {34}, - pages = {34}, - doi = {10.3847/1538-4365/ac416c}, -archivePrefix = {arXiv}, - eprint = {2109.10352}, - primaryClass = {astro-ph.IM}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2022ApJS..258...34R}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - - +@ARTICLE{COMPAS:2021, + author = {{Team COMPAS: Riley}, Jeff and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold}, + title = "{Rapid Stellar and Binary Population Synthesis with COMPAS}", + journal = {\apjs}, + keywords = {1622, 154, 1108, 162}, + year = 2022, + month = feb, + volume = {258}, + number = {2}, + eid = {34}, + pages = {34}, + doi = {10.3847/1538-4365/ac416c}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2022ApJS..258...34R}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/online-docs/COMPAS-2025methodsPaper.bib b/online-docs/COMPAS-2025methodsPaper.bib new file mode 100644 index 000000000..970b3074d --- /dev/null +++ b/online-docs/COMPAS-2025methodsPaper.bib @@ -0,0 +1,18 @@ +@ARTICLE{COMPAS:2025, + author = {{Team COMPAS: Mandel}, Ilya and {Riley}, Jeff and {Boesky}, Adam and {Brcek}, Adam and {Hirai}, Ryosuke and {Kapil}, Veome and {Lau}, Mike Y.~M. and {Merritt}, JD and {Rodr{\'\i}guez-Segovia}, Nicol{\'a}s and {Romero-Shaw}, Isobel and {Song}, Yuzhe and {Stevenson}, Simon and {Vajpeyi}, Avi and {van Son}, L.~A.~C. and {Vigna-G{\'o}mez}, Alejandro and {Willcox}, Reinhold}, + title = "{Rapid Stellar and Binary Population Synthesis with COMPAS: Methods Paper II}", + journal = {\apjs}, + keywords = {Binary stars, Stellar populations, Stellar evolution, Stellar evolutionary models, Stellar remnants, 154, 1622, 1599, 2046, 1627, Solar and Stellar Astrophysics, High Energy Astrophysical Phenomena, Instrumentation and Methods for Astrophysics}, + year = 2025, + month = sep, + volume = {280}, + number = {1}, + eid = {43}, + pages = {43}, + doi = {10.3847/1538-4365/adf8d0}, +archivePrefix = {arXiv}, + eprint = {2506.02316}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2025ApJS..280...43T}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/online-docs/README.rst b/online-docs/README.rst index eaa3dc1b5..fc44fcecd 100644 --- a/online-docs/README.rst +++ b/online-docs/README.rst @@ -37,15 +37,12 @@ In the repository root directory: View results in 'docs/online-docs/_build/html/index.html' -Make sure there arnt any broken links! See the build logs: +Make sure there aren't any broken links! See the build logs: -``` -(pages/User guide/docker: line 22) ok https://stackoverflow.com/questions/23735149/what-is-the-difference-between-a-docker-image-and-a-container -(pages/Developer guide/Developer build/docker-developer: line 23) ok https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment -(pages/User guide/Running COMPAS/running-via-docker: line 41) broken https://stackoverflow.com/questions/23735149/what-is-the-difference-between-a-docker-image-and-a-container#:~:text=An%20instance%20of%20an%20image,of%20layers%20as%20you%20describe.&text=You%20can%20see%20all%20your,an%20image%20is%20a%20container - Anchor '%3A~%3Atext%3DAn%20instance%20of%20an%20image%2Cof%20layers%20as%20you%20describe.%26text%3DYou%20can%20see%20all%20your%2Can%20image%20is%20a%20container' not found -(pages/User guide/docker: line 49) ok https://www.docker.com/ - -``` +.. (pages/User guide/docker: line 22) ok https://stackoverflow.com/questions/23735149/what-is-the-difference-between-a-docker-image-and-a-container +.. (pages/Developer guide/Developer build/docker-developer: line 23) ok https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment +.. (pages/User guide/Running COMPAS/running-via-docker: line 41) broken https://stackoverflow.com/questions/23735149/what-is-the-difference-between-a-docker-image-and-a-container#:~:text=An%20instance%20of%20an%20image,of%20layers%20as%20you%20describe.&text=You%20can%20see%20all%20your,an%20image%20is%20a%20container - Anchor '%3A~%3Atext%3DAn%20instance%20of%20an%20image%2Cof%20layers%20as%20you%20describe.%26text%3DYou%20can%20see%20all%20your%2Can%20image%20is%20a%20container' not found +.. (pages/User guide/docker: line 49) ok https://www.docker.com/ Pushing the changes online diff --git a/online-docs/pages/Developer guide/Services/Program options/services-program-options.rst b/online-docs/pages/Developer guide/Services/Program options/services-program-options.rst index 5c08836a3..154908183 100644 --- a/online-docs/pages/Developer guide/Services/Program options/services-program-options.rst +++ b/online-docs/pages/Developer guide/Services/Program options/services-program-options.rst @@ -30,7 +30,7 @@ Adding new program options To add a new program option, take the following steps (these are duplicated at the top of the ``Options.cpp`` source file): -1. Decide on a string for the option - this is the string the user will use on the commandline or in the grid file (e.g. "random-seed"). The convention is hyphenated lower case. Try to be consistent with existing option names. For example, if you are adding a new prescription for something, make sure the option name ends with "-prescription", or if you are adding a new distribution for something, make sure the option name ends with "-distribution". */ +1. Decide on a string for the option - this is the string the user will use on the commandline or in the grid file (e.g. "random-seed"). The convention is hyphenated lower case. Try to be consistent with existing option names. For example, if you are adding a new prescription for something, make sure the option name ends with "-prescription", or if you are adding a new distribution for something, make sure the option name ends with "-distribution". 2. Decide on a class member variable name for the option (e.g. m_RandomSeed). diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-ce.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-ce.rst index f7030a61d..1b05ecab5 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-ce.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-ce.rst @@ -1,81 +1,86 @@ -BSE common envelopes -==================== - -Default record definition for the BSE Common Envelopes log file:: - - const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { - BINARY_PROPERTY::RANDOM_SEED, - BINARY_PROPERTY::TIME, - STAR_1_PROPERTY::LAMBDA_AT_COMMON_ENVELOPE, - STAR_2_PROPERTY::LAMBDA_AT_COMMON_ENVELOPE, - STAR_1_PROPERTY::BINDING_ENERGY_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::BINDING_ENERGY_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::ECCENTRICITY_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::ECCENTRICITY_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::MASS_1_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::MASS_1_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::MASS_ENV_1, - BINARY_PROPERTY::RADIUS_1_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::RADIUS_1_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::STELLAR_TYPE_1_PRE_COMMON_ENVELOPE, - STAR_1_PROPERTY::STELLAR_TYPE, - STAR_1_PROPERTY::LAMBDA_FIXED, - STAR_1_PROPERTY::LAMBDA_NANJING, - STAR_1_PROPERTY::LAMBDA_LOVERIDGE, - STAR_1_PROPERTY::LAMBDA_LOVERIDGE_WINDS, - STAR_1_PROPERTY::LAMBDA_KRUCKOW, - STAR_1_PROPERTY::BINDING_ENERGY_FIXED, - STAR_1_PROPERTY::BINDING_ENERGY_NANJING, - STAR_1_PROPERTY::BINDING_ENERGY_LOVERIDGE, - STAR_1_PROPERTY::BINDING_ENERGY_LOVERIDGE_WINDS, - STAR_1_PROPERTY::BINDING_ENERGY_KRUCKOW, - BINARY_PROPERTY::MASS_2_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::MASS_2_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::MASS_ENV_2, - BINARY_PROPERTY::RADIUS_2_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::RADIUS_2_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::STELLAR_TYPE_2_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::STELLAR_TYPE, - STAR_2_PROPERTY::LAMBDA_FIXED, - STAR_2_PROPERTY::LAMBDA_NANJING, - STAR_2_PROPERTY::LAMBDA_LOVERIDGE, - STAR_2_PROPERTY::LAMBDA_LOVERIDGE_WINDS, - STAR_2_PROPERTY::LAMBDA_KRUCKOW, - STAR_2_PROPERTY::BINDING_ENERGY_FIXED, - STAR_2_PROPERTY::BINDING_ENERGY_NANJING, - STAR_2_PROPERTY::BINDING_ENERGY_LOVERIDGE, - STAR_2_PROPERTY::BINDING_ENERGY_LOVERIDGE_WINDS, - STAR_2_PROPERTY::BINDING_ENERGY_KRUCKOW, - BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, - BINARY_PROPERTY::STELLAR_MERGER, - BINARY_PROPERTY::OPTIMISTIC_COMMON_ENVELOPE, - BINARY_PROPERTY::COMMON_ENVELOPE_EVENT_COUNT, - BINARY_PROPERTY::DOUBLE_CORE_COMMON_ENVELOPE, - STAR_1_PROPERTY::IS_RLOF, - STAR_1_PROPERTY::LUMINOSITY_PRE_COMMON_ENVELOPE, - STAR_1_PROPERTY::TEMPERATURE_PRE_COMMON_ENVELOPE, - STAR_1_PROPERTY::DYNAMICAL_TIMESCALE_PRE_COMMON_ENVELOPE, - STAR_1_PROPERTY::THERMAL_TIMESCALE_PRE_COMMON_ENVELOPE, - STAR_1_PROPERTY::NUCLEAR_TIMESCALE_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::IS_RLOF, - STAR_2_PROPERTY::LUMINOSITY_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::TEMPERATURE_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::DYNAMICAL_TIMESCALE_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::THERMAL_TIMESCALE_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::NUCLEAR_TIMESCALE_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::ZETA_STAR, - BINARY_PROPERTY::ZETA_LOBE, - BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE, - BINARY_PROPERTY::CIRCULARIZATION_TIMESCALE, - STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE_PRE_COMMON_ENVELOPE, - STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE_PRE_COMMON_ENVELOPE, - BINARY_PROPERTY::IMMEDIATE_RLOF_POST_COMMON_ENVELOPE, - BINARY_PROPERTY::SIMULTANEOUS_RLOF - }; - +BSE common envelopes +==================== + +Default record definition for the BSE Common Envelopes log file:: + + const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { + BINARY_PROPERTY::RANDOM_SEED, + BINARY_PROPERTY::TIME, + STAR_1_PROPERTY::LAMBDA_AT_COMMON_ENVELOPE, + STAR_2_PROPERTY::LAMBDA_AT_COMMON_ENVELOPE, + STAR_1_PROPERTY::BINDING_ENERGY_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::BINDING_ENERGY_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::ECCENTRICITY_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::ECCENTRICITY_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_STAGE_1_CE, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::MASS_1_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::MASS_1_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::MASS_ENV_1, + BINARY_PROPERTY::RADIUS_1_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::RADIUS_1_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::STELLAR_TYPE_1_PRE_COMMON_ENVELOPE, + STAR_1_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::LAMBDA_CONVECTIVE_ENVELOPE, + STAR_1_PROPERTY::LAMBDA_FIXED, + STAR_1_PROPERTY::LAMBDA_NANJING, + STAR_1_PROPERTY::LAMBDA_LOVERIDGE, + STAR_1_PROPERTY::LAMBDA_LOVERIDGE_WINDS, + STAR_1_PROPERTY::LAMBDA_KRUCKOW, + STAR_1_PROPERTY::BINDING_ENERGY_FIXED, + STAR_1_PROPERTY::BINDING_ENERGY_NANJING, + STAR_1_PROPERTY::BINDING_ENERGY_LOVERIDGE, + STAR_1_PROPERTY::BINDING_ENERGY_LOVERIDGE_WINDS, + STAR_1_PROPERTY::BINDING_ENERGY_KRUCKOW, + STAR_1_PROPERTY::CONVECTIVE_ENV_MASS, + BINARY_PROPERTY::MASS_2_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::MASS_2_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::MASS_ENV_2, + BINARY_PROPERTY::RADIUS_2_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::RADIUS_2_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::STELLAR_TYPE_2_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::LAMBDA_CONVECTIVE_ENVELOPE, + STAR_2_PROPERTY::LAMBDA_FIXED, + STAR_2_PROPERTY::LAMBDA_NANJING, + STAR_2_PROPERTY::LAMBDA_LOVERIDGE, + STAR_2_PROPERTY::LAMBDA_LOVERIDGE_WINDS, + STAR_2_PROPERTY::LAMBDA_KRUCKOW, + STAR_2_PROPERTY::BINDING_ENERGY_FIXED, + STAR_2_PROPERTY::BINDING_ENERGY_NANJING, + STAR_2_PROPERTY::BINDING_ENERGY_LOVERIDGE, + STAR_2_PROPERTY::BINDING_ENERGY_LOVERIDGE_WINDS, + STAR_2_PROPERTY::BINDING_ENERGY_KRUCKOW, + STAR_2_PROPERTY::CONVECTIVE_ENV_MASS, + BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, + BINARY_PROPERTY::STELLAR_MERGER, + BINARY_PROPERTY::OPTIMISTIC_COMMON_ENVELOPE, + BINARY_PROPERTY::COMMON_ENVELOPE_EVENT_COUNT, + BINARY_PROPERTY::DOUBLE_CORE_COMMON_ENVELOPE, + STAR_1_PROPERTY::IS_RLOF, + STAR_1_PROPERTY::LUMINOSITY_PRE_COMMON_ENVELOPE, + STAR_1_PROPERTY::TEMPERATURE_PRE_COMMON_ENVELOPE, + STAR_1_PROPERTY::DYNAMICAL_TIMESCALE_PRE_COMMON_ENVELOPE, + STAR_1_PROPERTY::THERMAL_TIMESCALE_PRE_COMMON_ENVELOPE, + STAR_1_PROPERTY::NUCLEAR_TIMESCALE_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::IS_RLOF, + STAR_2_PROPERTY::LUMINOSITY_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::TEMPERATURE_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::DYNAMICAL_TIMESCALE_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::THERMAL_TIMESCALE_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::NUCLEAR_TIMESCALE_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::ZETA_STAR, + BINARY_PROPERTY::ZETA_LOBE, + BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE, + BINARY_PROPERTY::CIRCULARIZATION_TIMESCALE, + STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE_PRE_COMMON_ENVELOPE, + STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE_PRE_COMMON_ENVELOPE, + BINARY_PROPERTY::IMMEDIATE_RLOF_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::SIMULTANEOUS_RLOF + }; + diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-detailed.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-detailed-output.rst similarity index 94% rename from online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-detailed.rst rename to online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-detailed-output.rst index b7e377b65..e57f25375 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-detailed.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-detailed-output.rst @@ -1,85 +1,88 @@ -BSE detailed output -=================== - -Default record definition for the BSE Detailed Output log file:: - - const ANY_PROPERTY_VECTOR BSE_DETAILED_OUTPUT_REC = { - BINARY_PROPERTY::RANDOM_SEED, - BINARY_PROPERTY::DT, - BINARY_PROPERTY::TIME, - BINARY_PROPERTY::UNBOUND, - BINARY_PROPERTY::SEMI_MAJOR_AXIS_RSOL, - BINARY_PROPERTY::ECCENTRICITY, - STAR_1_PROPERTY::MZAMS, - STAR_2_PROPERTY::MZAMS, - STAR_1_PROPERTY::MASS_0, - STAR_2_PROPERTY::MASS_0, - STAR_1_PROPERTY::MASS, - STAR_2_PROPERTY::MASS, - STAR_1_PROPERTY::ENV_MASS, - STAR_2_PROPERTY::ENV_MASS, - STAR_1_PROPERTY::CORE_MASS, - STAR_2_PROPERTY::CORE_MASS, - STAR_1_PROPERTY::HE_CORE_MASS, - STAR_2_PROPERTY::HE_CORE_MASS, - STAR_1_PROPERTY::CO_CORE_MASS, - STAR_2_PROPERTY::CO_CORE_MASS, - STAR_1_PROPERTY::RADIUS, - STAR_2_PROPERTY::RADIUS, - BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1, - BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2, - STAR_1_PROPERTY::OMEGA, - STAR_2_PROPERTY::OMEGA, - STAR_1_PROPERTY::OMEGA_BREAK, - STAR_2_PROPERTY::OMEGA_BREAK, - STAR_1_PROPERTY::INITIAL_STELLAR_TYPE, - STAR_2_PROPERTY::INITIAL_STELLAR_TYPE, - STAR_1_PROPERTY::STELLAR_TYPE, - STAR_2_PROPERTY::STELLAR_TYPE, - STAR_1_PROPERTY::AGE, - STAR_2_PROPERTY::AGE, - STAR_1_PROPERTY::LUMINOSITY, - STAR_2_PROPERTY::LUMINOSITY, - STAR_1_PROPERTY::TEMPERATURE, - STAR_2_PROPERTY::TEMPERATURE, - STAR_1_PROPERTY::ANGULAR_MOMENTUM, - STAR_2_PROPERTY::ANGULAR_MOMENTUM, - STAR_1_PROPERTY::DYNAMICAL_TIMESCALE, - STAR_2_PROPERTY::DYNAMICAL_TIMESCALE, - STAR_1_PROPERTY::THERMAL_TIMESCALE, - STAR_2_PROPERTY::THERMAL_TIMESCALE, - STAR_1_PROPERTY::NUCLEAR_TIMESCALE, - STAR_2_PROPERTY::NUCLEAR_TIMESCALE, - STAR_1_PROPERTY::ZETA_SOBERMAN, - STAR_2_PROPERTY::ZETA_SOBERMAN, - STAR_1_PROPERTY::ZETA_SOBERMAN_HE, - STAR_2_PROPERTY::ZETA_SOBERMAN_HE, - STAR_1_PROPERTY::ZETA_HURLEY, - STAR_2_PROPERTY::ZETA_HURLEY, - STAR_1_PROPERTY::ZETA_HURLEY_HE, - STAR_2_PROPERTY::ZETA_HURLEY_HE, - STAR_1_PROPERTY::MASS_LOSS_DIFF, - STAR_2_PROPERTY::MASS_LOSS_DIFF, - STAR_1_PROPERTY::DOMINANT_MASS_LOSS_RATE, - STAR_2_PROPERTY::DOMINANT_MASS_LOSS_RATE, - STAR_1_PROPERTY::MASS_TRANSFER_DIFF, - STAR_2_PROPERTY::MASS_TRANSFER_DIFF, - BINARY_PROPERTY::TOTAL_ANGULAR_MOMENTUM, - BINARY_PROPERTY::TOTAL_ENERGY, - STAR_1_PROPERTY::METALLICITY, - STAR_2_PROPERTY::METALLICITY, - BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, - STAR_1_PROPERTY::PULSAR_MAGNETIC_FIELD, - STAR_2_PROPERTY::PULSAR_MAGNETIC_FIELD, - STAR_1_PROPERTY::PULSAR_SPIN_PERIOD, - STAR_2_PROPERTY::PULSAR_SPIN_PERIOD, - STAR_1_PROPERTY::PULSAR_SPIN_DOWN_RATE, - STAR_2_PROPERTY::PULSAR_SPIN_DOWN_RATE, - STAR_1_PROPERTY::PULSAR_BIRTH_PERIOD, - STAR_2_PROPERTY::PULSAR_BIRTH_PERIOD, - STAR_1_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, - STAR_2_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, - STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE, - STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE - }; - +BSE detailed output +=================== + +Default record definition for the BSE Detailed Output log file:: + + const ANY_PROPERTY_VECTOR BSE_DETAILED_OUTPUT_REC = { + BINARY_PROPERTY::RANDOM_SEED, + BINARY_PROPERTY::DT, + BINARY_PROPERTY::TIME, + BINARY_PROPERTY::UNBOUND, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_RSOL, + BINARY_PROPERTY::ECCENTRICITY, + STAR_1_PROPERTY::MZAMS, + STAR_2_PROPERTY::MZAMS, + STAR_1_PROPERTY::MASS_0, + STAR_2_PROPERTY::MASS_0, + STAR_1_PROPERTY::MASS, + STAR_2_PROPERTY::MASS, + STAR_1_PROPERTY::ENV_MASS, + STAR_2_PROPERTY::ENV_MASS, + STAR_1_PROPERTY::CORE_MASS, + STAR_2_PROPERTY::CORE_MASS, + STAR_1_PROPERTY::HE_CORE_MASS, + STAR_2_PROPERTY::HE_CORE_MASS, + STAR_1_PROPERTY::CO_CORE_MASS, + STAR_2_PROPERTY::CO_CORE_MASS, + STAR_1_PROPERTY::RADIUS, + STAR_2_PROPERTY::RADIUS, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2, + STAR_1_PROPERTY::OMEGA, + STAR_2_PROPERTY::OMEGA, + STAR_1_PROPERTY::OMEGA_BREAK, + STAR_2_PROPERTY::OMEGA_BREAK, + STAR_1_PROPERTY::INITIAL_STELLAR_TYPE, + STAR_2_PROPERTY::INITIAL_STELLAR_TYPE, + STAR_1_PROPERTY::STELLAR_TYPE, + STAR_2_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::AGE, + STAR_2_PROPERTY::AGE, + STAR_1_PROPERTY::LUMINOSITY, + STAR_2_PROPERTY::LUMINOSITY, + STAR_1_PROPERTY::TEMPERATURE, + STAR_2_PROPERTY::TEMPERATURE, + STAR_1_PROPERTY::ANGULAR_MOMENTUM, + STAR_2_PROPERTY::ANGULAR_MOMENTUM, + STAR_1_PROPERTY::DYNAMICAL_TIMESCALE, + STAR_2_PROPERTY::DYNAMICAL_TIMESCALE, + STAR_1_PROPERTY::THERMAL_TIMESCALE, + STAR_2_PROPERTY::THERMAL_TIMESCALE, + STAR_1_PROPERTY::NUCLEAR_TIMESCALE, + STAR_2_PROPERTY::NUCLEAR_TIMESCALE, + STAR_1_PROPERTY::ZETA_SOBERMAN, + STAR_2_PROPERTY::ZETA_SOBERMAN, + STAR_1_PROPERTY::ZETA_SOBERMAN_HE, + STAR_2_PROPERTY::ZETA_SOBERMAN_HE, + STAR_1_PROPERTY::ZETA_HURLEY, + STAR_2_PROPERTY::ZETA_HURLEY, + STAR_1_PROPERTY::ZETA_HURLEY_HE, + STAR_2_PROPERTY::ZETA_HURLEY_HE, + STAR_1_PROPERTY::MASS_LOSS_DIFF, + STAR_2_PROPERTY::MASS_LOSS_DIFF, + STAR_1_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_2_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_1_PROPERTY::MASS_TRANSFER_DIFF, + STAR_2_PROPERTY::MASS_TRANSFER_DIFF, + BINARY_PROPERTY::TOTAL_ANGULAR_MOMENTUM, + BINARY_PROPERTY::TOTAL_ENERGY, + STAR_1_PROPERTY::METALLICITY, + STAR_2_PROPERTY::METALLICITY, + BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, + STAR_1_PROPERTY::PULSAR_MAGNETIC_FIELD, + STAR_2_PROPERTY::PULSAR_MAGNETIC_FIELD, + STAR_1_PROPERTY::PULSAR_SPIN_PERIOD, + STAR_2_PROPERTY::PULSAR_SPIN_PERIOD, + STAR_1_PROPERTY::PULSAR_SPIN_DOWN_RATE, + STAR_2_PROPERTY::PULSAR_SPIN_DOWN_RATE, + STAR_1_PROPERTY::PULSAR_BIRTH_PERIOD, + STAR_2_PROPERTY::PULSAR_BIRTH_PERIOD, + STAR_1_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, + STAR_2_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, + STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE, + STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE, + BINARY_PROPERTY::RLOF_MASS_LOSS_RATE, + BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, + BINARY_PROPERTY::RLOF_ACCRETION_EFFICIENCY + }; + diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-switchlog.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-switchlog.rst index 7e95f38f2..204d20eaf 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-switchlog.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-switchlog.rst @@ -1,87 +1,96 @@ -BSE switchlog -============= - -Default record definition for the BSE SwitchLog log file:: - - const ANY_PROPERTY_VECTOR BSE_SWITCH_LOG_REC = { - BINARY_PROPERTY::RANDOM_SEED, - BINARY_PROPERTY::TIME - }; - - -The default record specification can be modified at runtime via a logfile record specifications file (program option ``--logfile-definitions``). -See :doc:`standard-logfiles-record-specification` for details. - -Note that the BSE SwitchLog file has the following columns automatically appended to each record: - - - The constituent star switching stellar type: 1 = Primary, 2 = Secondary. - - The stellar type from which the star is switching. - - The stellar type to which the star is switching. - -|br| -**STAR_SWITCHING** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - :class: aligned-text - - * - Data type: - - INT - * - COMPAS variable: - - `derived from` ``BaseBinaryStar::m_Star1/m_Star2`` - * - Description: - - The constituent star switching stellar type, where 1 = Primary, and 2 = Secondary. - * - Header String: - - "STAR_SWITCHING" - -**SWITCHING_FROM** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - :class: aligned-text - - * - Data type: - - INT - * - COMPAS variable: - - `derived from` ``BaseStar::m_StellarType`` - * - Description: - - The stellar type of the constituent star immediately prior to the switch. - * - Header String: - - "SWITCHING_FROM" - -**SWITCHING_TO** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - :class: aligned-text - - * - Data type: - - INT - * - COMPAS variable: - - Not applicable - * - Description: - - The stellar type to which the constituent star will switch (i.e. the stellar type immediately following the switch). - * - Header String: - - "SWITCHING_TO" - -**IS_MERGER** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - :class: aligned-text - - * - Data type: - - BOOL - * - COMPAS variable: - - Not applicable - * - Description: - - Flag to indicate if the switchlog record records a merger (rather than a simple switch) - * - Header String: - - "IS_MERGER" - -These columns will always be automatically appended to each BSE Switch Log record: they cannot be removed via the log file record -specifications file. +BSE switchlog +============= + +Default record definition for the BSE SwitchLog log file:: + + const ANY_PROPERTY_VECTOR BSE_SWITCH_LOG_REC = { + BINARY_PROPERTY::RANDOM_SEED, + BINARY_PROPERTY::TIME, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_RSOL, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2, + STAR_1_PROPERTY::MASS, + STAR_2_PROPERTY::MASS, + STAR_1_PROPERTY::STELLAR_TYPE, + STAR_2_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::RADIUS, + STAR_2_PROPERTY::RADIUS + }; + + +The default record specification can be modified at runtime via a logfile record specifications file (program option ``--logfile-definitions``). +See :doc:`standard-logfiles-record-specification` for details. + +Note that the BSE SwitchLog file has the following columns automatically appended to each record: + + - The constituent star switching stellar type: 1 = Primary, 2 = Secondary. + - The stellar type from which the star is switching. + - The stellar type to which the star is switching. + +|br| +**STAR_SWITCHING** + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + :class: aligned-text + + * - Data type: + - INT + * - COMPAS variable: + - `derived from` ``BaseBinaryStar::m_Star1/m_Star2`` + * - Description: + - The constituent star switching stellar type, where 1 = Primary, and 2 = Secondary. + * - Header String: + - "STAR_SWITCHING" + +**SWITCHING_FROM** + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + :class: aligned-text + + * - Data type: + - INT + * - COMPAS variable: + - `derived from` ``BaseStar::m_StellarType`` + * - Description: + - The stellar type of the constituent star immediately prior to the switch. + * - Header String: + - "SWITCHING_FROM" + +**SWITCHING_TO** + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + :class: aligned-text + + * - Data type: + - INT + * - COMPAS variable: + - Not applicable + * - Description: + - The stellar type to which the constituent star will switch (i.e. the stellar type immediately following the switch). + * - Header String: + - "SWITCHING_TO" + +**IS_MERGER** + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + :class: aligned-text + + * - Data type: + - BOOL + * - COMPAS variable: + - Not applicable + * - Description: + - Flag to indicate if the switchlog record records a merger (rather than a simple switch) + * - Header String: + - "IS_MERGER" + +These columns will always be automatically appended to each BSE Switch Log record: they cannot be removed via the log file record +specifications file. diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-system-snapshot.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-system-snapshot.rst new file mode 100644 index 000000000..def0e08c1 --- /dev/null +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-BSE-system-snapshot.rst @@ -0,0 +1,88 @@ +BSE system snapshot +=================== + +Default record definition for the BSE System Snapshot log file:: + + const ANY_PROPERTY_VECTOR BSE_SYSTEM_SNAPSHOT_LOG_REC = { + BINARY_PROPERTY::RANDOM_SEED, + BINARY_PROPERTY::DT, + BINARY_PROPERTY::TIME, + BINARY_PROPERTY::UNBOUND, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_RSOL, + BINARY_PROPERTY::ECCENTRICITY, + STAR_1_PROPERTY::MZAMS, + STAR_2_PROPERTY::MZAMS, + STAR_1_PROPERTY::MASS_0, + STAR_2_PROPERTY::MASS_0, + STAR_1_PROPERTY::MASS, + STAR_2_PROPERTY::MASS, + STAR_1_PROPERTY::ENV_MASS, + STAR_2_PROPERTY::ENV_MASS, + STAR_1_PROPERTY::CORE_MASS, + STAR_2_PROPERTY::CORE_MASS, + STAR_1_PROPERTY::HE_CORE_MASS, + STAR_2_PROPERTY::HE_CORE_MASS, + STAR_1_PROPERTY::CO_CORE_MASS, + STAR_2_PROPERTY::CO_CORE_MASS, + STAR_1_PROPERTY::RADIUS, + STAR_2_PROPERTY::RADIUS, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2, + STAR_1_PROPERTY::OMEGA, + STAR_2_PROPERTY::OMEGA, + STAR_1_PROPERTY::OMEGA_BREAK, + STAR_2_PROPERTY::OMEGA_BREAK, + STAR_1_PROPERTY::INITIAL_STELLAR_TYPE, + STAR_2_PROPERTY::INITIAL_STELLAR_TYPE, + STAR_1_PROPERTY::STELLAR_TYPE, + STAR_2_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::AGE, + STAR_2_PROPERTY::AGE, + STAR_1_PROPERTY::LUMINOSITY, + STAR_2_PROPERTY::LUMINOSITY, + STAR_1_PROPERTY::TEMPERATURE, + STAR_2_PROPERTY::TEMPERATURE, + STAR_1_PROPERTY::ANGULAR_MOMENTUM, + STAR_2_PROPERTY::ANGULAR_MOMENTUM, + STAR_1_PROPERTY::DYNAMICAL_TIMESCALE, + STAR_2_PROPERTY::DYNAMICAL_TIMESCALE, + STAR_1_PROPERTY::THERMAL_TIMESCALE, + STAR_2_PROPERTY::THERMAL_TIMESCALE, + STAR_1_PROPERTY::NUCLEAR_TIMESCALE, + STAR_2_PROPERTY::NUCLEAR_TIMESCALE, + STAR_1_PROPERTY::ZETA_SOBERMAN, + STAR_2_PROPERTY::ZETA_SOBERMAN, + STAR_1_PROPERTY::ZETA_SOBERMAN_HE, + STAR_2_PROPERTY::ZETA_SOBERMAN_HE, + STAR_1_PROPERTY::ZETA_HURLEY, + STAR_2_PROPERTY::ZETA_HURLEY, + STAR_1_PROPERTY::ZETA_HURLEY_HE, + STAR_2_PROPERTY::ZETA_HURLEY_HE, + STAR_1_PROPERTY::MASS_LOSS_DIFF, + STAR_2_PROPERTY::MASS_LOSS_DIFF, + STAR_1_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_2_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_1_PROPERTY::MASS_TRANSFER_DIFF, + STAR_2_PROPERTY::MASS_TRANSFER_DIFF, + BINARY_PROPERTY::TOTAL_ANGULAR_MOMENTUM, + BINARY_PROPERTY::TOTAL_ENERGY, + STAR_1_PROPERTY::METALLICITY, + STAR_2_PROPERTY::METALLICITY, + BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, + STAR_1_PROPERTY::PULSAR_MAGNETIC_FIELD, + STAR_2_PROPERTY::PULSAR_MAGNETIC_FIELD, + STAR_1_PROPERTY::PULSAR_SPIN_PERIOD, + STAR_2_PROPERTY::PULSAR_SPIN_PERIOD, + STAR_1_PROPERTY::PULSAR_SPIN_DOWN_RATE, + STAR_2_PROPERTY::PULSAR_SPIN_DOWN_RATE, + STAR_1_PROPERTY::PULSAR_BIRTH_PERIOD, + STAR_2_PROPERTY::PULSAR_BIRTH_PERIOD, + STAR_1_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, + STAR_2_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, + STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE, + STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE, + BINARY_PROPERTY::RLOF_MASS_LOSS_RATE, + BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, + BINARY_PROPERTY::RLOF_ACCRETION_EFFICIENCY + }; + diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-detailed.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-detailed-output.rst similarity index 100% rename from online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-detailed.rst rename to online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-detailed-output.rst diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-switchlog.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-switchlog.rst index 7d9434e8b..9ecd23cc4 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-switchlog.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-switchlog.rst @@ -1,54 +1,57 @@ -SSE switchlog -============= - -Default record definition for the SSE SwitchLog log file:: - - const ANY_PROPERTY_VECTOR SSE_SWITCH_LOG_REC = { - STAR_PROPERTY::RANDOM_SEED, - STAR_PROPERTY::TIME - }; - - -The default record specification can be modified at runtime via a logfile record specifications file (program option ``--logfile-definitions``). -See :doc:`standard-logfiles-record-specification` for details. - -Note that the SSE SwitchLog file has the following columns automatically appended to each record: - - - The stellar type from which the star is switching. - - The stellar type to which the star is switching. - -|br| -**SWITCHING_FROM** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - :class: aligned-text - - * - Data type: - - INT - * - COMPAS variable: - - `derived from` ``BaseStar::m_StellarType`` - * - Description: - - The stellar type of the star immediately prior to the switch. - * - Header String: - - "SWITCHING_FROM" - -**SWITCHING_TO** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - :class: aligned-text - - * - Data type: - - INT - * - COMPAS variable: - - Not applicable - * - Description: - - The stellar type to which the star will switch (i.e. the stellar type immediately following the switch). - * - Header String: - - "SWITCHING_TO" - -These columns will always be automatically appended to each SSE Switch Log record: they cannot be removed via the log file record -specifications file. +SSE switchlog +============= + +Default record definition for the SSE SwitchLog log file:: + + const ANY_PROPERTY_VECTOR SSE_SWITCH_LOG_REC = { + STAR_PROPERTY::RANDOM_SEED, + STAR_PROPERTY::TIME, + STAR_PROPERTY::MASS, + STAR_PROPERTY::STELLAR_TYPE, + STAR_PROPERTY::RADIUS + }; + + +The default record specification can be modified at runtime via a logfile record specifications file (program option ``--logfile-definitions``). +See :doc:`standard-logfiles-record-specification` for details. + +Note that the SSE SwitchLog file has the following columns automatically appended to each record: + + - The stellar type from which the star is switching. + - The stellar type to which the star is switching. + +|br| +**SWITCHING_FROM** + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + :class: aligned-text + + * - Data type: + - INT + * - COMPAS variable: + - `derived from` ``BaseStar::m_StellarType`` + * - Description: + - The stellar type of the star immediately prior to the switch. + * - Header String: + - "SWITCHING_FROM" + +**SWITCHING_TO** + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + :class: aligned-text + + * - Data type: + - INT + * - COMPAS variable: + - Not applicable + * - Description: + - The stellar type to which the star will switch (i.e. the stellar type immediately following the switch). + * - Header String: + - "SWITCHING_TO" + +These columns will always be automatically appended to each SSE Switch Log record: they cannot be removed via the log file record +specifications file. diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-system-snapshot.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-system-snapshot.rst new file mode 100644 index 000000000..b79091fac --- /dev/null +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications-SSE-system-snapshot.rst @@ -0,0 +1,25 @@ +SSE system snapshot +=================== + +Default record definition for the SSE System Snapshot log file:: + + const ANY_PROPERTY_VECTOR SSE_SYSTEM_SNAPSHOT_LOG_REC = { + STAR_PROPERTY::AGE, + STAR_PROPERTY::DT, + STAR_PROPERTY::TIME, + STAR_PROPERTY::STELLAR_TYPE, + STAR_PROPERTY::METALLICITY, + STAR_PROPERTY::MASS_0, + STAR_PROPERTY::MASS, + STAR_PROPERTY::RADIUS, + STAR_PROPERTY::RZAMS, + STAR_PROPERTY::LUMINOSITY, + STAR_PROPERTY::TEMPERATURE, + STAR_PROPERTY::CORE_MASS, + STAR_PROPERTY::CO_CORE_MASS, + STAR_PROPERTY::HE_CORE_MASS, + STAR_PROPERTY::MDOT, + STAR_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_PROPERTY::TIMESCALE_MS + }; + diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications.rst index 622192308..786733766 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-default-record-specifications.rst @@ -9,22 +9,26 @@ For Single Star Evolution (SSE): .. toctree:: :maxdepth: 1 - standard-logfiles-default-record-specifications-SSE-detailed - standard-logfiles-default-record-specifications-SSE-pulsars + standard-logfiles-default-record-specifications-SSE-system-snapshot + standard-logfiles-default-record-specifications-SSE-sysparms + standard-logfiles-default-record-specifications-SSE-detailed-output standard-logfiles-default-record-specifications-SSE-supernovae + standard-logfiles-default-record-specifications-SSE-pulsars standard-logfiles-default-record-specifications-SSE-switchlog - standard-logfiles-default-record-specifications-SSE-sysparms For Binary Star Evolution (BSE): .. toctree:: :maxdepth: 1 + standard-logfiles-default-record-specifications-BSE-system-snapshot standard-logfiles-default-record-specifications-BSE-sysparms - standard-logfiles-default-record-specifications-BSE-detailed + standard-logfiles-default-record-specifications-BSE-detailed-output standard-logfiles-default-record-specifications-BSE-supernovae standard-logfiles-default-record-specifications-BSE-dco standard-logfiles-default-record-specifications-BSE-ce standard-logfiles-default-record-specifications-BSE-pulsars standard-logfiles-default-record-specifications-BSE-rlof standard-logfiles-default-record-specifications-BSE-switchlog + + diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst index a1e7ad748..18b7541b0 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-binary.rst @@ -790,6 +790,38 @@ Binary Properties * - Header String: - SemiMajorAxis>MT +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR1_LUM** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→luminosity1 + * - Description: + - Luminosity (\ :math:`L_\odot`) of the primary immediately after RLOF. + * - Header String: + - Lum(1)>MT + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR2_LUM** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→luminosity2 + * - Description: + - Luminosity (\ :math:`L_\odot`) of the secondary immediately after RLOF. + * - Header String: + - Lum(2)>MT + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -854,6 +886,38 @@ Binary Properties * - Header String: - Radius(2)>MT +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR1_TEFF** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→temperature1 + * - Description: + - Effective temperature (\ :math:`K`) of the primary immediately after RLOF. + * - Header String: + - Teff(1)>MT + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **RLOF_POST_MT_STAR2_TEFF** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_RLOFDetails.propsPostMT→temperature2 + * - Description: + - Effective temperature (\ :math:`K`) of the secondary immediately after RLOF. + * - Header String: + - Teff(2)>MT + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -1026,6 +1090,39 @@ but not both. If both are printed then the file will contain two columns with th * - Header String: - SemiMajorAxisCE +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **SEMI_MAJOR_AXIS_POST_STAGE_1_CE** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_CEDetails.postCEE.semiMajorAxisAfterStage1 + * - Description: + - Semi-major axis immediately following stage 1 of a 2-stage common envelope event (zero for other common envelope prescriptions) (\ :math:`R_\odot`). + * - Header String: + - SemiMajorAxisStage1>CE + + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -1801,16 +1947,32 @@ both. If both are printed then the file will contain two columns with the same h :header-rows: 0 :class: aligned-text - * - :cspan:`2` **SYNCHRONIZATION_TIMESCALE** + * - :cspan:`2` **SYNCHRONIZATION_TIMESCALE_1** - * - Data type: - DOUBLE * - COMPAS variable: - - BaseBinaryStar::m_SynchronizationTimescale + - BaseBinaryStar::m_SynchronizationTimescale1 * - Description: - - Tidal synchronisation timescale (Myr). + - Tidal synchronisation timescale for the primary star (Myr). * - Header String: - - Tau_Sync + - Tau_Sync(1) + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **SYNCHRONIZATION_TIMESCALE_2** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseBinaryStar::m_SynchronizationTimescale2 + * - Description: + - Tidal synchronisation timescale for the secondary star (Myr). + * - Header String: + - Tau_Sync(2) .. flat-table:: :widths: 25 75 1 1 @@ -1880,6 +2042,103 @@ both. If both are printed then the file will contain two columns with the same h :ref:`Back to Top ` +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **TIDAL_POTENTIAL_LOVE_NUMBER_10_1, TIDAL_POTENTIAL_LOVE_NUMBER_12_1, TIDAL_POTENTIAL_LOVE_NUMBER_22_1, TIDAL_POTENTIAL_LOVE_NUMBER_32_1** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - `derived from` BaseStar::CalculateImKnmTidal() + * - Description: + - l=2 components of the tidal potential Love number for the primary star, indexed by (n,m). + * - Header String: + - ImKnm1_10, ImKnm1_12, ImKnm1_22, ImKnm1_32 + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **TIDAL_POTENTIAL_LOVE_NUMBER_10_2, TIDAL_POTENTIAL_LOVE_NUMBER_12_2, TIDAL_POTENTIAL_LOVE_NUMBER_22_2, TIDAL_POTENTIAL_LOVE_NUMBER_32_2** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - `derived from` BaseStar::CalculateImKnmTidal() + * - Description: + - l=2 components of the tidal potential Love number for the secondary star, indexed by (n,m). + * - Header String: + - ImKnm2_10, ImKnm2_12, ImKnm2_22, ImKnm2_32 + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_1, TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_1, TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_1, TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_1** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - `derived from` BaseStar::CalculateImKnmEquilibrium() + * - Description: + - l=2 components of the equilibrium tidal potential Love number for the primary star, indexed by (n,m). + * - Header String: + - ImKnm1_10_eq, ImKnm1_12_eq, ImKnm1_22_eq, ImKnm1_32_eq + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_2, TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_2, TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_2, TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_2** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - `derived from` BaseStar::CalculateImKnmEquilibrium() + * - Description: + - l=2 components of the equilibrium tidal potential Love number for the secondary star, indexed by (n,m). + * - Header String: + - ImKnm2_10_eq, ImKnm2_12_eq, ImKnm2_22_eq, ImKnm2_32_eq + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_1, TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_1, TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_1, TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_1** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - `derived from` BaseStar::CalculateImKnmDynamical() + * - Description: + - l=2 components of the dynamical tidal potential Love number for the primary star, indexed by (n,m). + * - Header String: + - ImKnm1_10_dyn, ImKnm1_12_dyn, ImKnm1_22_dyn, ImKnm1_32_dyn + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_2, TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_2, TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_2, TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_2** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - `derived from` BaseStar::CalculateImKnmDynamical() + * - Description: + - l=2 components of the dynamical tidal potential Love number for the secondary star, indexed by (n,m). + * - Header String: + - ImKnm2_10_dyn, ImKnm2_12_dyn, ImKnm2_22_dyn, ImKnm2_32_dyn + + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-options.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-options.rst index f62f57653..cdb25945a 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-options.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-options.rst @@ -608,7 +608,7 @@ DEPRECATION NOTICE: property ``BLACK_HOLE_KICKS`` has been deprecated and will s * - COMPAS variable: - Options::m_InitialMassFunctionMax * - Description: - - Value of program option ``--initial-mass-max`` + - Value of program option ``--initial-mass-function-max`` * - Header String: - PO_Initial_Mass_Func_Max @@ -624,7 +624,7 @@ DEPRECATION NOTICE: property ``BLACK_HOLE_KICKS`` has been deprecated and will s * - COMPAS variable: - Options::m_InitialMassFunctionMin * - Description: - - Value of program option ``--initial-mass-min`` + - Value of program option ``--initial-mass-function-min`` * - Header String: - PO_Initial_Mass_Func_Min @@ -1293,14 +1293,14 @@ DEPRECATION NOTICE: property ``LBV_PRESCRIPTION`` has been deprecated and will s :header-rows: 0 :class: aligned-text - * - :cspan:`2` **MINIMUM_MASS_SECONDARY** + * - :cspan:`2` **MINIMUM_SAMPLED_SECONDARY_MASS** - * - Data type: - DOUBLE * - COMPAS variable: - - Options::m_MinimumMassSecondary + - Options::m_MinimumSampledSecondaryMass * - Description: - - Value of program option ``--minimum-secondary-mass`` + - Value of program option ``--minimum-sampled-secondary-mass`` * - Header String: - PO_Min_Secondary_Mass diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-stellar.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-stellar.rst index 2d8237e90..0fb1b63da 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-stellar.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification-stellar.rst @@ -84,6 +84,23 @@ Following is an alphabetical list of stellar properties available for inclusion * - Header Strings: - Binding_Energy@CE(1), Binding_Energy@CE(2), Binding_Energy@CE(SN), Binding_Energy@CE(CP) +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + + * - :cspan:`2` **BINDING_ENERGY_CONVECTIVE_ENVELOPE** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - + * - Description: + - Absolute value of the binding energy of the convective portion of the envelope, via the :cite:`Picker2024` formalism, used for calculating post-CE separation in the 2-stage formalism (erg). + * - Header Strings: + - BE_ConvectiveEnvelope + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -94,9 +111,9 @@ Following is an alphabetical list of stellar properties available for inclusion * - Data type: - DOUBLE * - COMPAS variable: - - BaseStar::m_BindingEnergies.fixed + - * - Description: - - Absolute value of the envelope binding energy calculated using a fixed lambda parameter (erg). Calculated using lambda = m_Lambdas.fixed. + - Absolute value of the envelope binding energy calculated using a fixed lambda parameter (erg). * - Header Strings: - BE_Fixed, BE_Fixed(1), BE_Fixed(2), BE_Fixed(SN), BE_Fixed(CP) @@ -110,7 +127,7 @@ Following is an alphabetical list of stellar properties available for inclusion * - Data type: - DOUBLE * - COMPAS variable: - - BaseStar::m_BindingEnergies.kruckow + - * - Description: - Absolute value of the envelope binding energy calculated using the fit by :cite:`Vigna-Gomez2018` to :cite:`Kruckow2016` (erg). Calculated using alpha = OPTIONS→CommonEnvelopeSlopeKruckow(). * - Header Strings: @@ -126,9 +143,9 @@ Following is an alphabetical list of stellar properties available for inclusion * - Data type: - DOUBLE * - COMPAS variable: - - BaseStar::m_BindingEnergies.loveridge + - * - Description: - - Absolute value of the envelope binding energy calculated as per :cite:`Loveridge2011` (erg). Calculated using lambda = m_Lambdas.loveridge. + - Absolute value of the envelope binding energy calculated as per :cite:`Loveridge2011` (erg). * - Header Strings: - BE_Loveridge, BE_Loveridge(1), BE_Loveridge(2), BE_Loveridge(SN), BE_Loveridge(CP) @@ -142,9 +159,9 @@ Following is an alphabetical list of stellar properties available for inclusion * - Data type: - DOUBLE * - COMPAS variable: - - BaseStar::m_BindingEnergies.loveridgeWinds + - * - Description: - - Absolute value of the envelope binding energy calculated as per :cite:`Webbink1984` & :cite:`Loveridge2011` including winds (erg). Calculated using lambda = m_Lambdas.loveridgeWinds. + - Absolute value of the envelope binding energy calculated as per :cite:`Webbink1984` & :cite:`Loveridge2011` including winds (erg). * - Header Strings: - BE_Loveridge_Winds, BE_Loveridge_Winds(1), BE_Loveridge_Winds(2), BE_Loveridge_Winds(SN), BE_Loveridge_Winds(CP) @@ -158,9 +175,9 @@ Following is an alphabetical list of stellar properties available for inclusion * - Data type: - DOUBLE * - COMPAS variable: - - BaseStar::m_BindingEnergies.nanjing + - * - Description: - - Absolute value of the envelope binding energy calculated as per :doc:`Xu & Li (2010) <../../references>` (erg). Calculated using lambda = m_Lambdas.nanjing. + - Absolute value of the envelope binding energy calculated as per :doc:`Xu & Li (2010) <../../references>` (erg). * - Header Strings: - BE_Nanjing, BE_Nanjing(1), BE_Nanjing(2), BE_Nanjing(SN), BE_Nanjing(CP) @@ -274,26 +291,22 @@ Following is an alphabetical list of stellar properties available for inclusion - Mass CO_Core@\ CO, Mass_CO_Core@CO(1), Mass_CO_Core@CO(2), Mass_CO_Core@CO(SN), Mass_CO_Core@CO(CP) -.. flat-table:: - :widths: 25 75 1 1 +.. flat-table:: + :widths: 25 75 1 1 :header-rows: 0 :class: aligned-text - - * - :cspan:`2` **COMPONENT_SPEED** + + * - :cspan:`2` **CONVECTIVE_ENV_MASS** - * - Data type: - DOUBLE * - COMPAS variable: - - BaseStar::m_ComponentVelocity + - * - Description: - - Velocity of single star, equal to binary's Systemic Velocity for a bound binary (\ :math:`km s^{-1}`). - * - Header String: - - ComponentSpeed + - Envelope mass calculated using :cite:`Picker2024` (\ :math:`M\odot`). + * - Header Strings: + - Mass_Convective_Env, Mass_Convective_Env(1), Mass_Convective_Env(2) -.. flat-table:: - :widths: 25 75 1 1 - :header-rows: 0 - :class: aligned-text * - :cspan:`2` **CORE_MASS** - @@ -1116,6 +1129,23 @@ but not both. If both are printed then the file will contain two columns with th * - Header Strings: - Lambda@CE(1), Lambda@CE(2), Lambda@CE(SN), Lambda@CE(CP) + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **LAMBDA_CONVECTIVE_ENVELOPE** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - + * - Description: + - Common-envelope lambda parameter calculated using the :cite:`Picker2024` formalism for the convective portion of the envelope only. + * - Header Strings: + - Lambda_Convective + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -1789,22 +1819,6 @@ same header string.` * - Header Strings: - Recycled_NS, Recycled_NS(1), Recycled_NS(2), Recycled_NS(SN), Recycled_NS(CP) -.. flat-table:: - :widths: 25 75 1 1 - :header-rows: 0 - :class: aligned-text - - * - :cspan:`2` **RLOF_ONTO_NS** - - - * - Data type: - - DOUBLE - * - COMPAS variable: - - `derived from` BaseStar::m_SupernovaDetails.events.past - * - Description: - - Flag to indicate whether the star transferred mass to a neutron star at any time prior to the current timestep. - * - Header Strings: - - RLOF->NS, RLOF->NS(1), RLOF->NS(2), RLOF->NS(SN), RLOF->NS(CP) - .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -1883,6 +1897,24 @@ same header string.` * - Header Strings: - SN_Type, SN_Type(1), SN_Type(2), SN_Type(SN), SN_Type(CP) + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **SPEED** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseStar::m_ComponentVelocity.Magnitude() + * - Description: + - Magnitude of the velocity of a single star, or the binary's Systemic Velocity for a bound binary (\ :math:`km s^{-1}`). + * - Header String: + - ComponentSpeed + + .. flat-table:: :widths: 25 75 1 1 :header-rows: 0 @@ -2219,6 +2251,56 @@ or the other is printed in any file, but not both. If both are printed then the .. _stellar-props-V: +:ref:`Back to Top ` + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **VELOCITY_X** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseStar::m_ComponentVelocity.xValue() + * - Description: + - X-component of the velocity of a single star, or the binary's Systemic Velocity for a bound binary (\ :math:`km s^{-1}`). + * - Header String: + - VelocityX + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **VELOCITY_Y** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseStar::m_ComponentVelocity.yValue() + * - Description: + - Y-component of the velocity of a single star, or the binary's Systemic Velocity for a bound binary (\ :math:`km s^{-1}`). + * - Header String: + - VelocityY + +.. flat-table:: + :widths: 25 75 1 1 + :header-rows: 0 + :class: aligned-text + + * - :cspan:`2` **VELOCITY_Z** + - + * - Data type: + - DOUBLE + * - COMPAS variable: + - BaseStar::m_ComponentVelocity.zValue() + * - Description: + - Z-component of the velocity of a single star, or the binary's Systemic Velocity for a bound binary (\ :math:`km s^{-1}`). + * - Header String: + - VelocityZ + .. _stellar-props-W: .. _stellar-props-X: diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification.rst index 299b5fe51..d5742f7b0 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-specification.rst @@ -40,18 +40,20 @@ Log file definitions file specification ::= "{" { [ ] } "}" - ::= "SSE_SYSPARMS_REC" | # SSE only - "SSE_DETAILED_REC" | # SSE only - "SSE_SNE_REC" | # SSE only - "SSE_SWITCH_REC" | # SSE only - "BSE_SYSPARMS_REC" | # BSE only - "BSE_SWITCH_REC" | # BSE only - "BSE_DCO_REC" | # BSE only - "BSE_SNE_REC" | # BSE only - "BSE_CEE_REC" | # BSE only - "BSE_PULSARS_REC" | # BSE only - "BSE_RLOF_REC" | # BSE only - "BSE_DETAILED_REC" | # BSE only + ::= "SSE_SYSPARMS_REC" | # SSE only + "SSE_DETAILED_REC" | # SSE only + "SSE_SYS_SNAPSHOT_REC" | # SSE only + "SSE_SNE_REC" | # SSE only + "SSE_SWITCH_REC" | # SSE only + "BSE_SYSPARMS_REC" | # BSE only + "BSE_SWITCH_REC" | # BSE only + "BSE_DCO_REC" | # BSE only + "BSE_SNE_REC" | # BSE only + "BSE_CEE_REC" | # BSE only + "BSE_PULSARS_REC" | # BSE only + "BSE_RLOF_REC" | # BSE only + "BSE_DETAILED_REC" | # BSE only + "BSE_SYS_SNAPSHOT_REC" | # BSE only ::= "=" | "+=" | "-=" diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types-bse-system-snapshot.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types-bse-system-snapshot.rst new file mode 100644 index 000000000..7dceee959 --- /dev/null +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types-bse-system-snapshot.rst @@ -0,0 +1,7 @@ +BSE system snapshot file record types +===================================== + +Following is a list of the BSE System Snapshot file record type numbers and corresponding symbolic names, and their meaning: + +1. DEFAULT |BR| + Default BSE_SYSTEM_SNAPSHOT_LOG file record type diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types-sse-system-snapshot.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types-sse-system-snapshot.rst new file mode 100644 index 000000000..174f5f914 --- /dev/null +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types-sse-system-snapshot.rst @@ -0,0 +1,7 @@ +SSE system snapshot file record types +===================================== + +Following is a list of the SSE System Snapshot file record type numbers and corresponding symbolic names, and their meaning: + +1. DEFAULT |BR| + Default SSE_SYSTEM_SNAPSHOT_LOG file record type diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types.rst index 2007f34a4..249e2d7b9 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles-record-types.rst @@ -25,10 +25,9 @@ Each standard log file has its own set of record types - select a file below to standard-logfiles-record-types-sse-system-parameters standard-logfiles-record-types-sse-supernovae + standard-logfiles-record-types-sse-pulsar-evolution standard-logfiles-record-types-sse-detailed-output - standard-logfiles-record-types-sse-pulsars - standard-logfiles-record-types-sse - standard-logfiles-record-types-sse-switchlog + standard-logfiles-record-types-sse-system-snapshot standard-logfiles-record-types-bse-system-parameters standard-logfiles-record-types-bse-supernovae @@ -37,7 +36,7 @@ Each standard log file has its own set of record types - select a file below to standard-logfiles-record-types-bse-double-compact-objects standard-logfiles-record-types-bse-common-envelopes standard-logfiles-record-types-bse-detailed-output - standard-logfiles-record-types-bse-switchlog + standard-logfiles-record-types-bse-system-snapshot Since the record type property is an unsigned integer, filtering the output files by record type is very simple. Even so, users may want to disable the logging of some record types - perhaps to limit the size of the log files produced. For this reason, program options diff --git a/online-docs/pages/User guide/COMPAS output/standard-logfiles.rst b/online-docs/pages/User guide/COMPAS output/standard-logfiles.rst index cc7b47d88..e04efdbda 100644 --- a/online-docs/pages/User guide/COMPAS output/standard-logfiles.rst +++ b/online-docs/pages/User guide/COMPAS output/standard-logfiles.rst @@ -2,12 +2,12 @@ Standard log files ================== COMPAS defines several standard log files that may be produced depending upon the simulation mode (Single Star Evolution (SSE), -or Binary Star Evolution (BSE), see the ``--mode`` program option)), and the value of various program options. +or Binary Star Evolution (BSE), see the ``--mode`` program option), and the value of various program options. The standard log files are: .. list-table:: - :widths: 22 78 + :widths: 32 68 :header-rows: 0 :class: aligned-text @@ -19,7 +19,13 @@ The standard log files are: - Records summary information for all stars that experience a SN event during evolution. * - - - * - Detailed Output + * - System Snapshot Log + - Records detailed system information during evolution. + * - + - Enable with program option ``--system-snapshot-age-thresholds`` and/or ``--system-snapshot-time-thresholds``. + * - + - + * - BSE/SSE Detailed Output - Records detailed information for a star, or binary star, during evolution. * - - Enable with program option ``--detailed-output``. diff --git a/online-docs/pages/User guide/Post-processing/notebooks/CosmicIntegration.ipynb b/online-docs/pages/User guide/Post-processing/notebooks/CosmicIntegration.ipynb deleted file mode 100644 index 89caac777..000000000 --- a/online-docs/pages/User guide/Post-processing/notebooks/CosmicIntegration.ipynb +++ /dev/null @@ -1,564 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "1066be07", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "6414b33a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
✔️ 743 µs (2023-09-11T17:49:49/2023-09-11T17:49:49)
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%load_ext memory_profiler\n", - "%load_ext autotime" - ] - }, - { - "cell_type": "markdown", - "id": "f65f10e5", - "metadata": {}, - "source": [ - "# Cosmic Integration\n", - "\n", - "The duration from the birth of the binary until the merger as a double compact object (DCO) can range from a few million years (lifetime of the stars) to more than 100 giga years depending on the evolution of the system.\n", - "\n", - "Hence, \n", - "1. DCOs merge at different redshifts\n", - "2. Multiple DCOs merging at a specific redshift could have formed at different times.\n", - " \n", - "We thus need to know the star formation that went into forming a single system. However, the star formation rate is non-constant over the lifetime of the universe. Furthermore, star formation is heavily dependent on the metallicity of the star forming gas, which also changes over the lifetime of the universe. Combined, we call this the metallicity-specific star formation rate (MSSFR).\n", - "\n", - "**The cosmic-integration code predicts the merger rate of DCOs along a grid of redshifts and chirp-masses, assuming a model for the MSSFR.**\n", - "This tutorial covers how to use the COMPAS Cosmic Integration python tools (see [Neijssel et al. 2020](https://arxiv.org/abs/1906.08136) for derivations). \n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "f29267f5", - "metadata": {}, - "source": [ - "## Load COMPAS BBHs\n", - "\n", - "To run the Cosmic-integrator, we need a COMPAS data set with non-constant (preferably, randomly-sampled) metallicity and some number of double compact objects. \n", - "\n", - "In this tutorial we make a mock-COMPAS dataset. Some realistic example data can be downloaded from our [Zenodo database](https://zenodo.org/communities/compas/?page=1&size=20).\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "4b8a05df", - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
✔️ 1.89 s (2023-09-11T17:49:49/2023-09-11T17:49:51)
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABHMAAASKCAYAAADdSCKmAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXwU9eH/8fduCEl2JxAMyKGRSw4h3EWkRatfpMULVE4RBW9RtNUqgm1FWgTPSlv9AlqKFlSsiH49i9ifIh4VNZAgyFUFQQ4NV5jd3Du/PyhbY3Zmk+xukklez8eDx/ebfc9n5rObDDVvZj7jsSzLEgAAAAAAAFzBW9cTAAAAAAAAQNVR5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOGoXJkyfL4/HI4/EoOzu7Vo89b9688LE9Ho/y8/Nr9fgAAAAAgIaFMgeNRsuWLbVkyRLdf//9FV5fuXJluGhZtmxZpXElJSXq1KmTPB6P+vbtW+3jDh8+XEuWLNEll1xS06kDAAAAABDWpK4nANQWv9+viRMnVno9NzdXkpSamqpNmzZVyhcuXKhvvvlGktSnT59qH7d79+7q3r27tm/frpdeeqna4wEAAAAA+D6uzEGjl5eXp4yMDA0ZMkRffPFFhcw0Tc2ePVvnn3++pJqVOQAAAAAAxBNlDurEsGHDNHjwYH300Uc6++yz5ff7deqpp+qNN96QJL3xxhs644wz5Pf71bdvX3322WcJm0tubq569eqlXr16Vboy55FHHlFZWZmGDx8uiTIHAAAAAFD3KHNQJ/Ly8nTkyBGNGzdO55xzjubMmaOjR49qwoQJWrhwoW699VZdcskl+s1vfqN///vfuvrqqxMyj5KSEm3ZskW9e/dWdna2tm3bprKyMknSd999p0ceeUQzZszQzp07JUm9e/dOyDwAAAAAAKgq1sxBrfv222/17bffyuPxaN26dWrbtq0kyev16tZbb9W8efOUk5OjZs2aSZLy8/P16KOPqri4WCkpKXGdy6ZNm1RaWhouc0pLS7V9+3Z1795ds2fPVvPmzTV16lSNGjVKbdu2VatWreJ6fABoLN59912dc845euedd3T22WfHbb8ej0czZ87UvffeG7d9VsdNN92kbdu2adWqVXVyfFQ2ffp0vfPOO/r444/reioAACQMV+ag1uXl5UmS7r333nCRI0mGYUiSHnrooXCRI0nNmzeX1+uV1xv/H9fjc+nTp4969uwpj8ejL774Qjt27NCCBQs0a9YspaamKi8vj1usgEbsqaeeCj/17vifE088Ueecc47efPPNStv/cFu/368ePXpo9uzZCgaDFbadPHly+O+/SDwej6ZOnRp1jh06dKg0vzPPPLNBLLz+xhtv1FlZ4+Srr77SX/7yF919990VXt+/f7+uuuoqnXjiiUpLS1P//v31wgsvVHm/e/fu1fXXX6+OHTsqLS1NnTt31u23364DBw5U2O6HP2ff/zNs2LAK227fvl2jR49WixYt5PP5NGTIEL3zzjtVms+9995bYd9er1dt27bVhRdeqH/9618Vtt2xY0eluTRr1kx9+/bVY489pvLy8grbn3322crOzo543OP7evjhh6s0z+N++ctfKjc3V6+88kq1xgEA4CZcmYNat2HDBknSiBEjKry+ZcsWpaWlVfoP0K1bt6pz585KTk6WJM2fP19PPvmkNmzYoF//+tcx/Qd+bm6uvF6vsrOz5ff71bFjR23atEkrVqxQ586dNWnSJB06dEi7d+/W5ZdfXuPjAGgYfve736ljx46yLEv79+/XU089pfPPP1+vvvqqLrzwwgrbDhs2TFdeeaWkY4upr1mzRr/97W+Vm5tbrV/sq6Nv37761a9+JUnas2ePFi5cqEsvvVTz58/XjTfemJBj1oY33nhDjz/+eMS/7wsLC9WkSd3858wf//hHdezYUeecc074tYKCAg0ZMkT79+/XL37xC7Vp00Z///vfNXbsWD3zzDOaMGGC4z5N09TgwYMVCAR00003KSsrS7m5uXrsscf0zjvv6LPPPgv/48aSJUsqjf/000/1xz/+UT/72c/Cr+3atUuDBw9WUlKS7rzzTvn9fi1evFg/+9nP9M9//lNnnXVWld7v/PnzZRiGQqGQdu3apSeffFJnnXWW1q5dq759+1bY9rLLLgs/PODIkSN64403dMstt2jnzp166KGHqnS8mmrTpo1Gjhyphx9+uNJ/awAA0FBQ5qDW5eXlqW3btmrXrl2F13Nzc5WdnV3pVqrc3NwKa9W0bdtW9957r5599tm4zKVTp07y+/2SpOzs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tiNHjgw/hSoYDOpf//qXHn30UU2YMEHLly+vcGVLamqqXn311YjHdLqK44feeuutCr8UJyUl6YorrtADDzyg8vJyvfXWW7r44osrrCHStm1bTZgwQU8++aQKCgoqlBPXX399uMiRjv0yfvfdd+uNN96IS5nz/SInEAiosLBQP/7xj2VZltatW6dTTjmlWvur6Xv8/vfizDPP1KOPPqqdO3c6/u/DgQMHIl7tcu2112rBggUaO3asHn30UbVu3Vp///vfw08V+/4tZHZOOukknX766Tr//PPVvn17rVmzRn/605/UsmVL2yc7FRQUhIuOH96GPGXKFL366qsaN26c7rvvPvn9fv3v//6vPv300yrPSZJefPFFNWvWTJZl6ZtvvtH8+fM1atQovfXWW/rxj39cYdvrr79eY8aMCc/t//2//6f58+crJSVFjz76aIVtO3ToELHk3L9/f6UCLyMjQxs3btS2bduiXk3UokULrVu3rkrvDQAAt6HMQa0aM2aMLMuq9Pptt90W8TLoBx54QA888EBcjh0KhZSfn68mTZooIyNDd955p+68807HMV6vt9JjhI/r3bu3Vq5cqeHDh+vdd98Nv/7iiy/qvPPOC39dVFQk0zRt9wPAPU4//fQKCyBfdtll6tevn6ZOnaoLL7ywwsK2J598coUreUaMGKHMzEzdcccdeu2113TRRReFs6SkpEpX/dTEoEGDNHv2bHk8Hvl8Pp122mnhX+z37dunYDBY6eoKSTrttNPCTyj6/lUWP/xl2TAMtW3bVjt27Ih5rtKxtcfuuecevfLKK5VuqTly5Ei19/fdd99V+z3+sDA6XtBEusXnhyL971nv3r317LPP6sYbb9RPfvITScfWlpk3b56mTJni+Bh6Sfrggw/Cj/w+/rN28cUXq1mzZpo1a5auvvpq9ejRo9K4F198UUVFRRGfvHjeeefpz3/+s6ZPn67+/ftLkk499VTdd999mjZtWtQ5HXfWWWdVWAB59OjR6tKli2655ZZKiyh36dKlws/0pZdeKo/Ho3nz5unqq69Wr169wpnf74/48x/p5+x3v/udRo4cqa5duyo7O1vDhw/XFVdcEbF4sywrLrcDAgBQH7EAMlynrKxMRUVFKi8vr/D/R7Nr1y61atVKQ4YMics85s6dq1tuuUVvv/22LMtSUVGRZs2apS+//LLCJe4LFixQq1atauXpHQBql9fr1TnnnKO9e/dWaRHZoUOHSpLee++9hMynZcuWOvfcczV06FANHjw4LgvF11S0v5fLy8s1bNgwvf7667rrrrv08ssva9WqVXrqqackHSvga4Pd0wcjFTXfl5mZaVv4jB49Wnv27NHatWv10UcfaefOneErhY5f2WVn4cKFat26dYXSUDpWBlqWpQ8//DDiuGeeeUbNmzev9FS146ZOnar9+/frww8/1KeffqrNmzeHbyOLNic7hmFo0KBBysnJUSAQiLp9PH7+zzrrLP373//WX//6V2VnZ+svf/mL+vfvH/EWtEOHDlUonwAAaEi4MgeuM3v27Ar33N93331avHix41NTpk2bFr5Uu6r/AhlNz5499eKLL+rOO+/UxIkTlZSUpNGjR+vNN9+s8MvBqFGjlJ2dHf7aaQ0GAO5TVlYmSTJNM67bxlurVq3k8/m0ZcuWStnmzZvl9XqVlZVV4fVt27bpnHPOCX9tmqb27t2r888/P/xaixYtdPjw4QrjSkpKtHfvXsf5bNiwQVu3btXTTz+tK6+8Mvz6D59KJKnKV1fU5D3WVPfu3fXMM8/Yrq3TtGlTDRw4MPz122+/Lanyuks/tH///ohF2PHbd4//DH3f3r179c4772jy5MlKSUmx3bff76+wDs/bb7+ttLS08BVENfH9n2m/31/lbWNxwgkn6KqrrtJVV10l0zR11lln6d5779W1115bYbuvvvoq4tMzAQBoCChz4Dr33ntvxMfgOunRo0fEy9JjdfxWKydZWVlx++UBQP1SWlqqt956S02bNtVpp50Wdfvj6+LUxS+YSUlJ+tnPfqb/+7//044dO8Jrke3fv1/PPvushgwZUmkx3yeeeEJXXXVVeN2c+fPnq6ysrMKtpJ07d650pcUTTzwR9cqc46X396+AsSxLf/zjHytte7wkOHz4sOPVRjV5jzU1ePBgWZalzz77rMJjyCPZtm2bFixYoAsvvDDqVTBdu3bVW2+9pXfffVdnn312+PXnnntOkiot0i9Jy5YtUygUiniLlZ0PP/xQK1as0JQpU2r8jwwHDx7Uhx9+qDZt2ujEE0+Mun08fv4PHDgQfjqbdOwfaE499VTt2rWrwnZHjhzRv//9b02ZMqXGxwIAoD6jzAEAoIrefPNNbd68WZL07bff6tlnn9W2bds0ffr0SiXB1q1bw0+8O74A8tNPP61TTz1VV1xxRa3PXTp2ZeOqVas0ZMgQ3XTTTWrSpIkWLlyo4uJiPfjgg5W2Lykp0dChQzV27Fht2bJF//u//6shQ4ZUWPz42muv1Y033qhRo0Zp2LBhys3N1cqVK6Pe3tK9e3d17txZd9xxh7755hs1a9ZML774YsRblwYMGCBJuvXWW/Xzn/9cSUlJGj9+fFzeY00NGTJEmZmZevvttyuVOT169NCYMWN0yimn6KuvvtL8+fN1wgknaMGCBRW2e/fdd3XOOedo5syZ4X+kmDp1qhYvXqyLLrpIt9xyi9q3b6/Vq1frueee07Bhwyo9Wl06dotVu3btKpQ/37dz506NHTtWI0aMUJs2bbRx40YtWLBAvXv31pw5c6r8npcvXy7DMGRZlvbs2aNFixbp0KFDWrBgQaWrp3JycsI//0ePHtU///lPvfjii/rxj3+sn/3sZ1U+5g/16NFDZ599tgYMGKATTjhBn376qZYvXx5ebPy447dAjxw5ssbHAgCgXrMAAICjxYsXW5Iq/ElNTbX69u1rzZ8/3wqFQhW2/+G2SUlJ1sknn2xdf/311v79+ytsO2nSJMvv99seW5J18803R51j+/btrQsuuCDqdjk5OdbPf/5zyzAMy+fzWeecc4714YcfRny/q1evtq6//nqrRYsWlmEY1uWXX24dOHCgwrbl5eXWXXfdZbVs2dLy+XzWz3/+c2v79u1W+/btrUmTJoW3e+eddyxJ1jvvvBN+bdOmTda5555rGYZhtWzZ0rruuuus3NxcS5K1ePHi8HZlZWXWLbfcYrVq1cryeDzW9//zRZI1c+bMGr/HTz75pMLrkeZp59Zbb7VOPfXUSq+PHz/eysrKspo2bWq1a9fOuvHGGyt93y3Lsl599VVLkrVgwYIKr2/evNkaPXq0lZWVZSUnJ1vt27e37rjjDisQCFTax+bNmy1J1u233247z4MHD1ojR4602rRpYzVt2tTq2LGjddddd1kFBQVR36NlWdbMmTMr/Uz7/X5r8ODB1t///vcK23711VeVtm3SpInVqVMn684777SOHj1aYfuf/vSnVs+ePSMe9/i+HnroofBrs2fPtk4//XQrIyPDSktLs7p3727dd999VklJSYWx48aNs4YMGVKl9wcAgBt5LCvKCn8AAKBReeqpp3TVVVfpk08+qbQQL/7ryy+/VPfu3fXmm2+GF/etjmnTpum5557T9u3bHde6QfXs27dPHTt21LJly7gyBwDQYPE0KwAAgBro1KmTrrnmGt1///01Gv/OO+/ot7/9LUVOnM2bN0+9evWiyAEANGismQMAAFBD8+fPr/HYTz75JI4zwXE1LdcAAHATrswBAAAAAABwEdbMAQAAAAAAcBGuzAEAAAAAAHARyhwAAAAAAAAXocwBAAAAAABwEcocAAAAAAAAF6HMAQAAAAAAcBHKHAAAAAAAABehzAEAAAAAAHARyhwAAAAAAAAXocwBAAAAAABwEcocAAAAAAAAF6HMAQAAAAAAcBHKHAAAAAAAABehzAEAAAAAAHARyhwAAAAAAAAXaVLXE2gUDu+Sggfsc1+mlJFVe/MBAAAAAACu5bEsy6rrSTR497WVSoP2ebJPunkthQ4AAAAAAIiKK3NqQ2lQuvRJqWXXyln+VmnFdceu3KHMAQAAAAAAUVDm1JaWXaV2fet6FgAAAAAAwOVYABkAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFmtT1BPAf+VvtM1+mlJFVe3MBAAAAAAD1FmVOXfNlSsk+acV19tsk+6Sb11LoAAAAAAAAypw6l5F1rKgJHoic5289VvQED1DmAAAAAAAAypx6ISOLogYAAAAAAFQJCyADAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLNKnrCaCK8rfaZ75MKSOr9uYCAAAAAADqDGVOfefLlJJ90orr7LdJ9kk3r6XQAQAAAACgEaDMqe8yso4VNcEDkfP8rceKnuAByhwAAAAAABoByhw3yMiiqAEAAAAAAJJYABkAAAAAAMBVKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFmtT1BBAn+VvtM1+mlJFVe3MBAAAAAAAJQ5njdr5MKdknrbjOfptkn3TzWgodAAAAAAAaAMoct8vIOlbUBA9EzvO3Hit6ggcocwAAAAAAaAAocxqCjCyKGgAAAAAAGgkWQAYAAAAAAHARyhwAAAAAAAAXocwBAAAAAABwEcocAAAAAAAAF6HMAQAAAAAAcBHKHAAAAAAAABehzAEAAAAAAHARyhwAAAAAAAAXocwBAAAAAABwEcocAAAAAAAAF2lS1xNALcnfap/5MqWMrNqbCwAAAAAAqDHKnIbOlykl+6QV19lvk+yTbl5LoQMAAAAAgAtQ5jR0GVnHiprggch5/tZjRU/wAGUOAAAAAAAuQJnTGGRkUdQAAAAAANBAUObgGNbUAQAAAADAFShzGjvW1AEAAAAAwFUocxo71tQBAAAAAMBVKHPAmjoAAAAAALgIZQ6qhjV1AAAAAACoFyhz4Iw1dQAAAAAAqFcoc+CMNXUAAAAAAKhXvHU9AbhARpbUrm/kPy271uXMANSiyZMny+PxyOPxKDs7u1aPPW/evPCxPR6P8vPza/X4AAAAQH1CmYP4yN8q7Vkf+c/hXXU5MwBx1LJlSy1ZskT3339/hddXrlwZLlqWLVtWaVxJSYk6deokj8ejvn37Vvu4w4cP15IlS3TJJZfUdOoAAABAg8FtVohNVdfUGbdE8rW03we3aAGu4Pf7NXHixEqv5+bmSpJSU1O1adOmSvnChQv1zTffSJL69OlT7eN2795d3bt31/bt2/XSSy9VezwAAADQkFDmIDbR1tQJ5kvPXyEtHWW/DxZQBlwvLy9PGRkZ+tGPfqQvvviiQmaapmbPnq3zzz9fL7/8co3KHAAAAAD/RZmD2GVkORcxVVlA+euP7Lfhyh0gomHDhsk0Tf3hD3/QjBkz9Mknn6ht27b605/+pPPPP19vvPGGfve732nDhg3q0qWLFi1apAEDBiRkLrm5uerVq5d69eqllStXVsgeeeQRlZWVafjw4ZQ5AAAAQBxQ5iDxnMoeHn0O1FheXp4yMzM1btw4XXPNNbrkkks0Z84cTZgwQQ888IAeeughXXfddRo5cqTmzJmjq6++Onw7VDyVlJRoy5Ytuv7665Wdna3HHntMZWVlatKkib777js98sgjuueee7Rz505JUu/eveM+BwAAAKAxocxB3arqo8+drtyJhit70AB9++23+vbbb+XxeLRu3Tq1bdtWkuT1enXrrbdq3rx5ysnJUbNmzSRJ+fn5evTRR1VcXKyUlJS4zmXTpk0qLS1V7969lZ2drdLSUm3fvl3du3fX7Nmz1bx5c02dOlWjRo1S27Zt1apVq7geHwAAAGhsKHNQ92K9cieaaAswx4qyCHUgLy9PknTvvfeGixxJMgxDkvTQQw+FixxJat68ubxer7ze+D/E8Phc+vTpo549e8rj8eiLL75QamqqFixYoPnz5ys1NVV5eXncYgUAAADEAWUO6rdoV+5EU5UFmGMVa1lEGYQa2LBhgyRpxIgRFV7fsmWL0tLSNGzYsAqvb926VZ07d1ZycrKKi4s1ZcoUvf322zp8+LB69OihRx99VIMHD67RXHJzc+X1epWdnS2/36+OHTtq06ZNWrFihTp37qxJkybp0KFD2r17ty6//PKavWEAAAAAYZQ5qP+iLbAcTSxlUDTxKIsog1ADeXl5atu2rdq1a1fh9dzcXGVnZ1e6lSo3Nze8Vk1ZWZk6dOig999/XyeffLL+/ve/66KLLtKOHTvCV/ZUdy6dOnWS3++XJGVnZ2vFihVav369li9frqSkpPBaPZHWy9m9e7dmzpypNWvWKD09XePHj9ftt9+upKSkas8FAAAAaAwoc9DwxVoGRVPXVw4l+jayRGvXt65n4Ep2tyzl5ubqggsuqPBaaWmptmzZorFjx0qS/H6/7rnnnnB+vDzZsmVLjZ52lZeXpyFDhoS/zs7O1iuvvKJBgwbpkksuCW8jqdKcv/76aw0bNkzTp0/Xww8/rMOHD2vOnDkaN26cli9fXu25AAAAAI0BZU6CWZalo8WWdNSUCgrqejpIBG9zyWhes7FGJ+mKf0qFB2s2PnhAWnG9tOjSmo2vD2bsVnp6ujweT13PxDXKy8u1adOmSrdS5efna+/evZUKky+++CK8QHEk27Zt08GDB3XqqadWey779u3Tt99+W2Hfo0ePVnJyskaOHBl+LS8vTykpKerWrVuF8XfddZdmzpypCRMmSJJatGihJ598UiNGjNDrr79eqZgCAAAAQJmTcEePHlXz+49K959Z11MB6qf7m+vIkSMVFuuFs23btqmoqKhSaXP8VqYfvn78qphIZU5hYaEmTpyoGTNmqHnz6peSkW6f6tevn/r161dpu549e6pJk4r/s/PRRx9p6dKlkqQlS5aoS5cuOuOMMzRp0iStWrWKMgcAAACIgDInwdLT03XkyJG6nkbCFBQUKCsrS7t27eKXcQd8Ts7S09Pregqucnzx4x+WM3alzYYNG9SsWTN16NChwuulpaUaM2aMTj311Aq3XVWHU1F0XCgU0saNGzVu3LiI+fEnbC1atEgXXnihzjjjDDVt2lSlpaU1mhMAAADQ0Hksy7LqehJwr4KCAjVvzpUV0fA5ob4JhUKaMGGCAoGAXnrppUpXzEQyefJk/b//9/+Uk5OjJk2aKCMjI+Z5jBgxQjfddJOGDx9e6VjnnXdeuAAqKiqSaZp68MEH9dBDD+m7775Ty5YuXScKAAAAiJG3ricAAKh9N9xwg/bu3asXXnihSkXOcbt27VKrVq0qLHgci7lz5+qWW27R22+/LcuyVFRUpFmzZunLL7/U6NGjw9stWLBArVq10kMPPRSX4wIAAABuxpU5iAlXnFQNnxPqk507d6pDhw5KTU2t8PjvN998U2eeab++16ZNm7Rnzx5JkmEYOuOMM+Iyn7y8PN15553Kzc1VUlKSRo8erTlz5oQfdS4dK5G2bNkS/vqnP/2pkpOT43J8AAAAwG0ocxCT4uJizZ07VzNmzFBKSkpdT6fe4nMCAAAAAMQLZQ4AAAAAAICLsGYOAAAAAACAi1DmAAAAAAAAuEjVH2GCGgmFQtqzZ4/S09Pl8XjqejpAvWJZlo4ePap27drJ66VbBgAAAICqoMxJsD179igrK6uupwHUa7t27dLJJ59c19MAAAAAAFegzEmw9PR0Scd+WbV7JLVpmjIMw3Yf5OQNNS8oKFBWVlb4PAEAAAAAREeZk2DHb61q1qyZbZnj9XodfxkmJ2/IuSRuQQQAAACAamCRCgAAAAAAABehzAEAAAAAAHARyhwAAAAAAAAXocwBAAAAAABwERZAriWmacrrjdydBQIBx7Hk5A01N03TcSwAAAAAoDLKnFpiGIbjE32iPe2H3MW5Zcnw++vu+PU4D4VCjuMAAAAAAJVxmxUAAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIjyavJaZpyuuN3J0FAgHHseQNPA8G6/b4dZibpuk4FgAAAABQGWVOLTEMQ4ZhOObRxpM30Nzjqd/zS2AeCoUcxwEAAAAAKqPMAdA4HN4lBQ/Y575MKSOr9uYDAAAAADVEmQOg4Tu8S3r8dKnU4Za2ZJ9081oKHQAAAAD1HmUOgIYveOBYkXPpk1LLrpXz/K3SiuuObUeZAwAAAKCeo8wB0Hi07Cq161vXswAAAACAmPBocgAAAAAAABfhyhwADYPTAsf5W2t3LgAAAACQQJQ5tcQ0TXm9kS+ECgQCjmPJyRtqbpqm49gqq+oCx77M+BwPAAAAAOoQZU4tMQxDhmE45tHGk9cwtywZfn/dHZ/cNg+FQo7jqizaAscSjx4HAAAA0GBQ5gBoOFjgGAAAAEAjwALIAAAAAAAALkKZAwAAAAAA4CLcZhWFZVnyeDxV3r64uFjFxcXhrwsKChIxLQAAAAAA0EhR5vzH9u3btXz5ch05ckS9e/fWRRddJMMw5PF4qlXozJ07V7NmzUrwbAEAAAAAQGPFbVaSNm7cqIEDB+of//iHPvzwQ1155ZWaPHmyVq5cKUnhQqcqZsyYoSNHjoT/7Nq1K5FTBwAAAAAAjUyjvzKnsLBQ06dP1+WXX67HHntMkpSTk6MbbrhBDz/8sILBoC655JIqX5mTkpKilJSUSq+bpimvN3J3FggEHPdJnuA8GKzb4zfi3DRNx7EAAAAAgMoafZmTlpamgwcPasCAAZKkUCik/v37a8mSJZoyZYqeeOIJderUSX369InpOIZhyDAMxzzaePIE5n5/3R6/keahUMhxHAAAAACgskZ/m5VpmkpJSdH+/fslHVvwuKysTN27d9fjjz+uzz//XIsXL67jWQIAAAAAABzTKMucgwcPavPmzdq6dasMw9Dtt9+uhQsXasWKFUpKSpLX61Vpaal69OihBx98UH/729/09ddf1/W0AQAAAAAAGl+Z8/nnn+vcc8/V2LFjlZ2drd/97ncaNmyYpk6dqgkTJui1116T1+tVcnKyJCkjI0Nt2rSRP8ptOAAAAAAAALWhUZU5mzZt0tlnn62hQ4dq2bJlmjt3ru69914dOHBA06dP15VXXqlLL71UCxYs0L59+1RUVKT33ntPTZs2tV28GAAAAAAAoDY1mgWQ8/PzNWXKFE2cOFEPPfSQJOm0007TqlWrtGvXLvl8Pl133XXq16+ffvGLX+jBBx9Uenq69u7dq5UrV6pFixZ1/A6ARu7wLil4IHKWv7V25wIAAAAAdajRlDkej0fDhw/X6NGjw6/Nnj1bb731lvbu3avDhw+rR48e+sMf/qC8vDzl5ubKsiydccYZat++fR3OHIAk6fHTpVKHx8gn+yRfZmzHcCqFfJlSRlZs+wcAAACAOGg0ZU5mZqamTp2q9PR0SdKyZcs0c+ZMLVu2TOeee642bNigO+64Q8uWLdOsWbPUrVu3uB7fNE3bW7UCgYDjWPI6zoMOBUJtHL8B56ZpOo6toDQoXfqk1LJr5DyWssWXeawMWnGd/TbJPunmtRQ6AAAAAOpcoylzJIWLHEkaPHiwPv30U/Xv31+S9NOf/lStW7dWTk5OQo5tGIYMw3DMo40nr6Pc46nf83NxHgqFHMdV0rKr1K5v9cZURUbWsaLG6TauFdcdyylzAAAAANSxRlXmfF/79u3Dt0+FQiGVlJTIMAz17t27jmcGoE5kZFHUAAAAAHAFHtEkyev1as6cOfroo480ZsyYup4OAAAAAACArUZ7Zc5xL7zwglavXq1ly5Zp1apV6tKlS11PCQAAAAAAwFajvzKnR48e+u6777RmzRr169evrqcDAAAAAADgqNFfmdOzZ08tXbpUycnJdT0VAAAAAACAqBr9lTmSKHIAAAAAAIBrNPorc2qLaZryeiN3Z4FAwHEsOXlDzU3TdBwLAAAAAKiMMqeWGIYhwzAc82jjyW1Ylgy/v+6OX9e5i99/KBRyHAcAAAAAqIzbrAAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEV4NHktMU1TXm/k7iwQCDiOJSePKQ8G6/b4Drlpmo5jAQAAAACVUebUEsMwZBiGYx5tfIPNLUuG3x/beKfc40n88WMZn+g82vtP9PEd8lAo5DgOAAAAAFAZt1kBAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICL8GjyWmKaprzeyN1ZIBBwHEseYx4MNu7j1+PcNE3HsQAAAACAyihzaolhGDIMwzGPNt61uWXJ8Psd8pAMwyGXJcPvq/nxPZ7o84v2/pzmH+vxo41vwHkoFHIcBwAAAACojNusAAAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARXg0eS0xTVNeb+TuLBAIOI4lj5YH6/j45DXNTdN0HAsAAAAAqIwyp5YYhiHDMBzzaONdm1tWlDwkw+932LvHebzHW7fvzxNlfpYV5f3V8+9fAvNQKOQ4DgAAAABQGbdZAQAAAAAAuEiNrsx55ZVXqj1m2LBhSktLq8nhAAAAAAAA8B81KnMuvvjiam3v8Xi0bds2derUqSaHAwAAAAAAwH/U+Darffv2KRQKVemPz+eL55wBAAAAAAAarRqVOZMmTarWLVMTJ05Us2bNanIoAAAAAAAAfE+NbrNavHhxtbafP39+TQ4DAAAAAACAH+DR5LXENE15vZEvhAoEAo5jG34edM6DhQk+fox5MMr8Ez3exblpmo5jAQAAAACVJaTMCQQC2rp1q7KystSyZctEHMJ1DMOQYRiOebTx9Ta3QjL8DusiWSEZht9h75bz/j2eKOMlw++QW5Zzrjh8PrHs3+Op39/fBOahUMhxHAAAAACgsriXOXPmzNHq1at1+umna8uWLTIMQ48//jiPJQcAAAAAAIiDuJY5ixcv1sGDB7Vy5crway+88IJ++ctfauHChfE8FAAAAAAAQKNU40eTR/L8889r+vTpkqSrr75a+/fv15gxY/TJJ5/E8zAAAAAAAACNVlzLnOTkZJWUlEiSfvKTn8jnO7aOCutiAAAAAAAAxEdcy5xrr71W06ZNU3l5ua655hqlp6fr0Ucf1XnnnRfPwwAAAAAAADRacV0zZ+TIkTJNU0OHDlX79u21d+9e/fjHP9bs2bPjeRgAAAAAAIBGq8ZlzuzZs9W/f38NGDBArVu3Dr9++eWX6/LLL9fBgwfVokULeTyeuEzU7UzTlNcb+UKoQCDgOLb+50F3Hz+Y4Pknev8uzk3TdBwLAAAAAKisxmXOPffcEy5q2rRpEy52+vfvrx/96Edq165d3CbZEBiGIcMwHPNo4+ttHiqX4ffb55blnEfbv8dbhfk57N/jibL/KHnM84+y/2jjG3DOeloAAAAAUH01LnMGDhyovXv36qqrrlLLli2Vk5OjFStWaM6cOSovL1erVq3Uv39/vfHGG/GcLwAAAAAAQKNW4zLn448/1lNPPaW7775bAwcO1B/+8Ad17txZxcXFWr9+vXJycrRu3bp4zhUAAAAAAKDRi+lpVpMnT9bWrVvVrVs39e/fXzNmzFB5ebkGDRqkKVOm6IknnojXPAEAAAAAAKA4PJrcMAw9+OCD+vTTT/X555/r1FNP1d/+9rd4zA0AAAAAAAA/EHOZI0llZWUqLi7WZZddppNPPllXXXWVDh48GI9dAwAAAAAA4HtqvGbO/fffrw0bNmjDhg3avHmzUlNT1bt3b51++um64YYb1Lx583jOEwAAAAAAAIqhzLn77rvVoUMHTZo0SZdddpm6du0az3k1OKZpyuuNfCFUIBBwHFv3uSnJss+Dwdj2H3V8jHmwMMr4GD+fWN9/I85N03QcCwAAAACorMZlzplnnqn169dr1qxZevjhh9W7d2/1798//Cc7O1tJSUnxnKurGYYhwzAc82jjE5Zblgy/3z4PlcvwpTmPj+X4Hm+U8R7n+cW8/xg/P4+nbr9/Ls5DoZDjOAAAAABAZTUuc1avXi1J2rZtmz777DPl5OQoJydHzzzzjA4fPqyUlBT16tVLa9eujdtkAQAAAAAAGrsalznHdenSRV26dNH48ePDr3311Vf69NNPtW7dulh3DwAAAAAAgO+JucyJpGPHjurYsaPGjBmTiN0DAAAAAAA0WjV6NHleXl611rrYuHGjysrKanIoAAAAAAAAfE+Nypx+/frpwIEDVd5+8ODB+vrrr2tyKAAAAAAAAHxPjW6zsixLv/3tb+Xz+aq0fUlJSU0OAwAAAAAAgB+oUZlz1llnacuWLVXefvDgwUpLc3i0dSNgmqa83sgXQgUCAcextZNbNc+DhbEdP+bxwdjGk9dZbpqm41gAAAAAQGU1KnPefffdOE+j4TMMQ4ZhOObRxicsLy+VkZZin4fKZfidrsLyyPD77WPLcj6+xxsl98SWW5bz/FTHn38jzquz9hYAAAAA4JiEPM0KABqk/K32mS9TysiqvbkAAAAAaLQocwAgGl+mlOyTVlxnv02yT7p5LYUOAAAAgISjzAGAaDKyjhU1QZun+OVvPVb0BA9Q5gAAAABIOMocAKiKjCyKGgAAAAD1QuTHKwEAAAAAAKBeimuZU1hYqG+++abS6xs3boznYQAAAAAAABqtuN1mtXz5cv3yl79Uy5YtFQqF9OSTT2rQoEGSpCuuuEI5OTnxOpQrmaYprzdydxYIBBzHxie3nHPLIQ8WOu8/Wh4IxjY+GGV8rJ9PovdPbpuZpuk4FgAAAABQWdzKnNmzZ+uzzz5T69at9dlnn2nSpEm6++67NWHCBFkORUFjYRiGDMNwzKONr3EeKpfhS7PPy0pk+B1yj8d5/x6PDMNf8/HepCrsP4GfjyTD7zD/eOyfPOLroVDIcRwAAAAAoLK4lTmlpaVq3bq1JGnAgAF67733dMkll2j79u3yeDzxOgwAAAAAAECjFrc1c0488UTl5eWFvz7hhBO0atUqffHFFxVer++2b9+uTz75pMbji4uLVVBQUOEPAAAAAABAvMStzFmyZEn4ypzjmjZtqueee06rV6+O12ESav369RowYIDWr19f433MnTtXzZs3D//JyuJRxgAAAAAAIH5ivs2qoKBAixcv1r59+9SxY0f16dNHvXr1ks/nC2/zk5/8JNbDJFxubq5+8pOf6MYbb9R1111X4/3MmDFDt99+e/jrgoICCh0AAAAAABA3MZc5l156qXJzczVw4EC9+uqr2rJliySpc+fO6tOnj55//vmYJ5lo27Zt06BBg3THHXdo9uzZKi0t1T/+8Q/t27dPrVu31tChQ+WPskDucSkpKUpJSUnwjAEAAAAAQGMVc5nz0Ucf6d1339XAgQMlHVszZsOGDVq/fr1yc3NjnmCilZWV6bHHHpNhGOrbt68k6eKLL9bu3btVUFCgr7/+WqNGjdKMGTPUr1+/up0sAAAAAABo9GIuc3r37q0mTf67m5SUFP3oRz/Sj370o1h3XSuaNGmiqVOnqqioSI888ohuv/129erVS88//7zat2+vTZs2aeTIkZo3b56efvrpGh/HNE15vZGXKAoEAo5j45PbPx4+EAg6jw8WRckLJdk/sSwQjLL/aMevlc+HvC5y0zQdxwIAAAAAKou5zHnwwQd1zz33aPny5a66vai8vFxJSUmSpC5dumjatGmaNWuWMjMz9cgjj6hbt26Sjj1mff78+Ro5cqR+/etfq2vXrjU6nmEYMgzDMY823pYVkuH32edlxTLSHL43oZAMw/k2MsOfZh96k5zn5/FEeX8eGVFuY3N+/5bz+KrkMc0/xu9fI85DoZDjOAAAAABAZTGXOR06dFBBQYF69OihcePG6YwzzlC/fv3q9aK/W7du1auvvqoJEyaobdu2ko6t8TN79mxt2rRJnTp1kiRZliWPx6OSkhJ169ZNJ554Yl1OGwAAAAAAIPYyZ9SoUdq/f79++tOf6sMPP9T8+fNVUFCgE044Qf369dNbb70Vj3nGzfbt2zV48GAdOnRIBw4c0O23366WLVtKkk455RRlZWXJ4zl2y9Dx//uvf/1L7du3t71NCgAAAAAAoLbEXOZ8/vnn+uijj9SnT5/wazt27NC6deuUl5cX6+7jKhAIaO7cuRoxYoQGDhyoqVOnqqysTNOmTQsXOt+3ceNGPffcc3riiSf0/vvvq1mzZnUwawAAAAAAgP+KucwZOHBgpQVOO3TooA4dOuiSSy6Jdfdx5fV6NWDAAGVmZmrcuHFq2bKlxo8fL0nhQuf41Tg7duzQHXfcoa1bt2r16tXq1atXXU4dAAAAAABAUhzKnF/84he699579fe//10ZGRlxmFLipKWladKkSfL/ZzHcsWPHyrIsXXbZZbIsS9OnT1dmZqbKy8vl9/s1f/58eb1enXLKKXU8cwAAAAAAgGNiLnNGjx4t6dgToS655BINGjRI/fr1U3Z2tpo2bRrzBOPteJFTXl4ur9ercePGybIsTZgwQR6PR7/85S/18MMP66uvvtJzzz2n1NTUOp4xAAAAAADAf8Vc5nz11VfKzc3V+vXrlZubqzlz5mjHjh1q0qSJunXrVu/WzTkuKSlJlmUpFApp/Pjx8ng8uuKKK/TKK6/o3//+t9auXRvXIsc0TdsFlH94m1qNcsuyz4NBSU55kfP+g4XSf24/i5gXFkefn2MejHL8KHk8Pj/yOslN03QcCwAAAACoLOYyp3379mrfvr1GjBgRfu3o0aNav359vS1yjju+Po5lWRo3bpyeeOIJrV+/Xjk5OXFfI8cwDBmG4ZhHG2+rJCDD6SKotFQZPp/DBh4ZhkOe1MT5+J6jMvzR9u803hv9/f/niqqIrJAMwyH3RDm+ony+sY4nt81DoZDjOAAAAABAZTGXOZGkp6frzDPP1JlnnpmI3ceVx+NReXm57rzzTr3zzjtav349ix0DAAAAAIB6K/J9P41Qz549lZOTo969e9f1VAAAAAAAAGwl5Moct0lKStLVV18dvu0KAAAAAACgvuLKnP+gyAEAAAAAAG5AmQMAAAAAAOAi3GYFAPGSv9U+82VKGVm1NxcAAAAADRZlTi0xTVNeb+QLoQKBgOPYgGlKlmWfBwud88JiSfa3kQUKC52PX1TinAcCkqLML+r4WPJgjHmU/QdjHE9um5mm6TjWNXyZUrJPWnGd/TbJPunmtRQ6AAAAAGJGmVNLDMOQYRiOua3SoIymDjtPSZKR4rDmT5LzseXxyDD89rk3SYbfZ59bIef9y+M8XlaUPMrnEzX3yPA7vL+q7D/W8eQRXw+FQo7jXCMj61hREzwQOc/feqzoCR6gzAEAAAAQM8ocAIiHjCyKGgAAAAC1ggWQAQAAAAAAXIQyBwAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchKdZ1RLTNOX1Ru7OAoGAJMt2bCAQlBwe4RwIFjoNV6C0WJL9o8sDwULJ45QX2e/8+Pyc9h8IOI8PBquwf6c8yv5jPn6Cxzfi3DRNx7EAAAAAgMooc2qJYRgyDCNyWF4qIy3FfrBZJsPjUBikpcjwNbXPSyQjzSH3yH5u/9nA8Pucc8fxUfbvqcJ4x+PHuP+qHD+W8ZYlw++v+f4bcB5yKCkBAAAAAJFxmxUAAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgItQ5gAAAAAAALgIjyavJaZpyuuN3J0FAqYUKrMdGwiWSCH7b1WgpEROvVygpFySxz4vKrHNjs2v0DkPRskDgSjjHR67XpXxgVjH13Ee8/t3b26apuNYAAAAAEBllDm1xDAMGYYROTy4Q0bhAfvBTZvKSA7Z5038MvxpDnm5DL/PPvd4ZKSl2Ofy2M9dkrxJzrkkw/A77N5bhfEOeazjo+WWJcPvMP9Y9++J8vnGuv96nIdCDj/XAAAAAICIuM0KAAAAAADARShzAAAAAAAAXIQyBwAAAAAAwEVYMwcAakv+VvvMlyllZNXeXAAAAAC4FmUOACSaL1NK9kkrrrPfJtkn3byWQgcAAABAVJQ5AJBoGVnHipqgzVPr8rceK3qCByhzAAAAAERFmVNLzCOH5bUiP4Y5UFgsJXtsxwbKiiXZPzo8UBaUPPbLHwWKS+W0PFKgsFCSw/ELi6PkhbaZJAUCAec8GOP4QDDG8eR1lZum6Ti2QcnIoqgBAAAAEBeUObXEKPpGRvKRyGFpvoySo/aDM3rISLMvc1TeREaaw7eySVMZhmGfJzWRYfgd8qDzeK/XOZecc4/H+fjRxssjwx/L+Ci5ZVVh/jHsvxHnoVDkghMAAAAAYI+nWQEAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi1DmAAAAAAAAuAhlDgAAAAAAgIvwaPJaYppH5VXkxzAHSsqkJmm2YwPBQslr/60KlJZJHvteLlAScs6DRbZZ+Pix5IFAjHkwtvHBGMfX9f4bcG6apuPYRid/q33my5QysmpvLgAAAADqLcqcWmK8cbOM1KTI4TmzZXg99oPTmspoatnnTQ0Zhs8+LyqX4XfI5YmSyzn3eGUYhvN4x9zjnEfbvyfa+Ch51PlFyRO9/wach0KRC85Gx5cpJfukFdfZb5Psk25eS6EDAAAAgDIHAOpcRtaxoiZ4IHKev/VY0RM8QJkDAAAAgDIHAOqFjCyKGgAAAABVwgLIAAAAAAAALkKZAwAAAAAA4CKUOQAAAAAAAC5CmQMAAAAAAOAiLIBcS8yMHvKmJUfMAoVFUpMTbMcGgkWS1/5bFSgPSh77Xi5QXOI4t0BhkeSxfzR6IBCMPt4pDwSi5FH2H3U8uVtz0zQdxwIAAAAAKqPMqSXG7ndkpNgUJj1Gyij9zn5wxsn2YyUp1FRGqsNFVt5UGb4U+zwpWYZh2OfyyDD8DrFHht/nMF5R9i/n/Usy/FHyqPt3yC3Lef+x5rHOrwHnoVDIcRwAAAAAoDLKHABwi/yt9pkvk0ebAwAAAI0EZQ4A1He+TCnZJ624zn6bZJ9081oKHQAAAKARoMwBgPouI+tYURM8EDnP33qs6AkeoMwBAAAAGgHKHABwg4wsihoAAAAAkng0OQAAAAAAgKtwZQ4ANBQskAwAAAA0CpQ5tcRs2V/etMgfd6CoVEppYTs2ECySvPbfqkBZoeSxv8gqUFLmOLdAcZQ8EHDOg0FJ9o9OP5bHsP9ArOPrOI/5/Tfc3DRNx7GoIhZIBgAAABoVypxaYuSvl5FqU3ik3yAj2bIf7EuTkeJwR1xymgxfU/s8qakMX5p97i2V4Uuxz62QDMNvn3s8Mgyj5rkUZbzXObdCzu8v2v5jzRP9/htwHgqFHMehilggGQAAAGhUKHMAoCGoygLJ3IYFAAAANAiUOQDQ0HEbFgAAANCgUOYAQENX1duwvv7Ifhuu3AHqzOTJk/X0009Lknr27KnPP/+81o49b9483XbbbeGvv/vuO7Vs2bLWjg8AACKjzAGAxsDpNiyu3AHqvZYtW+rRRx9VRkZGpWzlypUaPny4JOm5557T+PHjK+QlJSXq3r27vvrqK/Xp00fr16+v8nGHDx+uli1basWKFXrppZdieQsAACCOKHMAoLFjAWU0YPv371fr1q2rtO23336rE088McEzqhm/36+JEydGzHJzcyVJqamp2rRpU6V84cKF+uabbyRJffr0qdZxu3fvru7du2v79u2UOQAA1CMOj0gCADQaGVlSu76R/7TsWpczA2Jy/fXXa+/evVG3W7Nmja699tpamFH85eXlKSMjQ0OGDNEXX3xRITNNU7Nnz9b5558vqfplDgAAqJ+4MqeWmCcOkDct8scdKCyWmmbajg0EiyRvkn1eVih57Xu5QFGZZPNU9GN5iX14/PgOOwgEg1HGR8kDgSh5jPsPFsZ4/Ch5zO+v8eamaTqORT0Ty9OwDu+yv/KnKuOBGsrJydF7772ncePG2W6zY8cOjRo1ShdddFEtzix+cnNz1atXL/Xq1UsrV66skD3yyCMqKyvT8OHD9fLLL1PmAADQQFDm1BKj6BsZsilk0prKsI7aD/ZnyUhNts8tnwzDZ583KZdh+O1zb1CG32G8x+s83iPnXJLhd8o9MgwjyvEdcstyzr1JzrkUW+6JMn/LivL+Yzy+i/NQKOQ4DvVEVdfUGbdE8kVYGDWYLz1/hVTqUHyyJg8S4Ntvv9Xu3bu1evVq2zLHNE2NGTNG3333nU466aRanmHsSkpKtGXLFl1//fXKzs7WY489prKyMjVp0kTfffedHnnkEd1zzz3auXOnJKl37951PGMAABAPlDkAAGfR1tQ5XtYsHWW/j2SfNPHFyGUPa/IgQdauXStJ+uc//xkxtyxLN9xwg0aOHKlPP/1UJ598cm1OLy42bdqk0tJS9e7dW9nZ2SotLdX27dvVvXt3zZ49W82bN9fUqVM1atQotW3bVq1atarrKQMAgDigzAEAROf0NCzJueyRqnYbldNtXNFwmxYi+OCDD9SrVy9t2LBBX3/9tU455ZQK+W9+8xuNGjVK+/fvl6QqXZnj8Xi0a9cu2+KnZ8+e+stf/qLBgwfH/gaqIC8vT9KxtXB69uwpj8ejL774QqmpqVqwYIHmz5+v1NRU5eXlcYsVAAANCGUOACB20coeJ1W5jSuaaLdpsWZPo7R27VpNnz5dl19+uV577TXddNNN4eyZZ55RamqqLr30Uv3617+WpLhcmbNx48aY91Edubm58nq9ys7Olt/vV8eOHbVp0yatWLFCnTt31qRJk3To0CHt3r1bl19+ea3ODQAAJA5lDgCgbkW7jSua47dpff1R5H1Udc0euzV/4oGyqNYdOXJEycnJOu+885SUlKQXXnghXOasXbtWK1eu1NNPPy1J2r17t6SqXZlTU8fXsYm3vLw8derUSf7/rM2WnZ2tFStWaP369Vq+fLmSkpLCjy7/4Xo5u3fv1syZM7VmzRqlp6dr/Pjxuv3225WUZP/QBQAAUD9Q5gAA6l6ir+xxWrOnKmv+xCrWK4eiade35mMbqDfffFPDhw9XixYtNGjQIL333nvauXOnmjRpolmzZumFF16Qx3PsSY27d+9WamqqWrb878/HV199palTp+pf//qXmjZtqhkzZujWW2+VJL366qt68MEHdfToUf3qV7/SjBkzwuM6dOigpUuXasiQIfJ4PPrzn/+sRx55RCeffLLWrFkjj8ejP/3pT3rooYdUUlKiX/3qV7rzzjtr/D7z8vI0ZMiQ8NfZ2dl65ZVXNGjQIF1yySXhbaSKjyX/+uuvNWzYME2fPl0PP/ywDh8+rDlz5mjcuHFavnx5jecDAABqB2UOAMDdqnJlT7QrY2K5MiiaeFw5FM29R2o+toF6+eWX9Yc//EGSNHnyZH344Yd6+OGH9eWXX2rBggXy+f77FMfdu3erXbt24a/Lysp0wQUXaOzYsXrxxRdVUlKibdu2hfP/9//+nzZs2KAdO3boRz/6kcaOHavOnTtHnMeqVauUm5ur5OT/PpXytdde0+eff649e/bonHPOUf/+/TV06NBqv8d9+/bp22+/rXDFzejRo5WcnKyRI0eGX8vLy1NKSoq6desWfu2uu+7SzJkzNWHCBElSixYt9OSTT2rEiBF6/fXXdcEFF1R7PgAAoPZQ5iSYZVmSpP0nDFIwrWnEbYqCkpmWZruPosNBmcXl9rlVKrOw2D4vLZcZtP8loaioRGYgYJ8Xl8oMOIwvLnbOi4qj7D/K8YuKZJqmcx7r+ETn9X1+dZQfPXpU0n/PEzuWZelosSUdNaWCAsdt0Uh5m0tGc+dtnH52qjK+psqaSuWp0nPX2m/TJE269G/HSqfqOrBdKihQenp6+EqTxqisrExLly7VmDFj9O233yopKSlc0Fx22WX61a9+pSeeeELvvPOOsrIqFnt79uxRr169wl9//PHHOnr0qO655x55vV6lpqZqwIAB4Xz69OkyDEPZ2dnq3bu3NmzYYFvmTJ8+Xc2aNavw2owZM9SsWTM1a9ZM11xzjZ5//vkalTmRbp/q16+f+vXrV2m7nj17VrjN66OPPtLSpUslSUuWLFGXLl10xhlnaNKkSVq1ahVlDgAA9RxlToId/2W1641/c9jqL7UzGaCeOnr0qJo3t/9F+ujRo2p+/1Hp/jNrcVZAbToqzb44hvFX6ciRI5VKg8Zk7dq1uuqqq/Tss8/qu+++07PPPhvODMPQhAkTNGjQIP34xz+uMC4UCqmwsFCbN2/Wyy+/rMLCQnm9XrVv315erzfisVq3bh3+/30+n2OhHWlR5e+XSVlZWeFSprqO3z71w7Vwvi8UCmnjxo0aN25cpez4+1u0aJEuvPBCnXHGGWratKlKS0trNB8AAFB7KHMSrF27dtq1a1eD/RfTgoICZWVladeuXY36l4ho+JwisyxLR48erXB7QyTp6ek6coTbSOKJn8nEqMvPNT09vVaPV99kZ2drwIABWrdunR577DGddtppFfI//vGPSklJqTTO6/Xqf//3f/Xss8/qrbfe0j333KMvv/xSO3fulGVZMf9vd6Txu3btCl/Js2vXLrVt2zbqfkKhkPLz89WkSRNlZGRIku68886o6+14vV4FI1yd27t3b61cuVLDhw/Xu+++G379xRdf1HnnnRf++vjVlZH2AQAA6g5lToJ5vd64POq0vjt+uTic8TlV5nRFznEej4fPLUH4mUwMPtfa16xZM3366ae2eaQi57jrr79e119/ffjrli1bKj09Xb///e81bdq08Jo537/VKhYPPPCA+vfvr7179+qvf/2r/vY3p6t3j9m1a5datWqlnj176vPPP495DnPnztXFF1+s+fPna+jQoSouLtYDDzygL7/8UqNHjw5vt2DBAt12220xHw8AAMQXZQ4AAMD3NGnSRK+99ppuuukmtW3bVqmpqfr1r38dtzLn/PPPV3Z2toqLi3Xbbbfp3HPPddx+2rRpmjhxoqRjt4zFQ8+ePfXiiy/qzjvv1MSJE5WUlKTRo0frzTffrPBo8lGjRik7Ozv8dVUKeAAAkHgeK9rKo4CDgoICNW/evNGv1RANnxPqG34mE4PPFdF4PB7t2rWrUVy1CwAAEifyyn5AFaWkpGjmzJmOl6+Dzwn1Dz+TicHnCgAAgNrAlTkAAAC1hCtzAABAPLBmDgAAQC3h39AAAEA8cJsVAAAAAACAi3BlToKFQiHt2bNH6enp8ng8dT0doF6xLEtHjx5Vu3bt5PXad8ucR4C9qp5HAAAAaDgocxJsz549ysrKqutpAPVatPUjOI+A6FiHBQAAoPGgzEmw9PR0Scf+I9vuMbWmacowDNt9kJM31LygoEBZWVnh88QO5xE5eeznEQAAABoOypwEO35LSLNmzWx/CfV6vY7/EU9O3pBzSVFvneI8IieP/TwCAABAw8HN9QAAAAAAAC5CmQMAAAAAAOAilDkAAAAAAAAuQpkDAAAAAADgIiyAXEtM05TXG7k7CwQCjmPJyRtqbpqm49hI23MekZNXVN3zCAAAAO5HmVNLDMNwfBJJtKeUkJM3xDwUCjmOi7QfziNy8oqqex4BAADA/bjNCgAAAAAAwEUocwAAAAAAAFyEMgcAAAAAAMBFKHMAAAAAAABchDIHAAAAAADARShzAAAAAAAAXIRHk9cS0zTl9UbuzgKBgONYcvKGmpum6Tg20vacR+TkFVX3PAIAAID7UebUEsMwZBiGYx5tPDl5Q8tDoZDjuEj74Twib5C5Zcnw+2s0vrrnEQAAANyPMgcAAFTN4V1S8IB97suUMrJqbz4AAACNFGUOAACI7vAu6fHTpdKg/TbJPunmtRQ6AAAACUaZAwAAogseOFbkXPqk1LJr5Tx/q7TiumPbUeYAAAAkFGUOAACoupZdpXZ963oWAAAAjRqPJgcAAAAAAHARyhwAAAAAAAAX4TarWmKaprzeyN1ZIBBwHEtO3lBz0zQdx0banvOInLyi6p5HAAAAcD/KnFpiGIYMw3DMo40nJ29oeSgUchwXaT+cR+QNMrcs59zjidt5BAAAAPfjNisAAAAAAAAXocwBAAAAAABwEcocAAAAAAAAF6HMAQAAAAAAcBHKHAAAAAAAABehzAEAAAAAAHARHk1eS0zTlNcbuTsLBAKOY8nJG2pumqbj2Ejbcx6RN8o8GLTNqnseAQAAwP0oc2qJYRgyDMMxjzaenDwiy5Lh99fd8WPIQ6GQ47hI++E8Iq9R7uLzRJLk8cTtPAIAAID7cZsVAAAAAACAi1DmAAAAAAAAuAhlThSWZdX1FAAAAAAAAMJYM+c/tm/fruXLl+vIkSPq3bu3LrroIhmGIY/HI8uy5PF4qrSf4uJiFRcXh78uKChI1JSBBovzCAAAAADscWWOpI0bN2rgwIH6xz/+oQ8//FBXXnmlJk+erJUrV0pSuNCpirlz56p58+bhP1lZWYmcOtAgcR4BAAAAgL1GX+YUFhZq+vTpuvzyy/Xuu+9q9erV+vjjj7Vz5049/PDDeumllySpylfmzJgxQ0eOHAn/2bVrVyKnDzRInEcAAAAAYK/R32aVlpamgwcPasCAAZKOPeK1f//+WrJkiaZMmaInnnhCnTp1Up8+faq0v5SUFKWkpFR63TRNeb2Ru7NAIOC4T3LymPJgsG6P75Cbphnxdc4j8lrP6/F5Ei23O48AAADQcDX6Msc0TaWkpGj//v2Sji14XFZWpu7du+vxxx/Xz3/+cy1evFjz5s2L6TiGYcgwDMc82njyBOWWJcPvr7vjJzr3eOrt/EKhkOO4SPvhPCJPWO7Svweqex4BAADA/RrlbVYHDx7U5s2btXXrVhmGodtvv10LFy7UihUrlJSUJK/Xq9LSUvXo0UMPPvig/va3v+nrr7+u62kDAAAAAAA0vjLn888/17nnnquxY8cqOztbv/vd7zRs2DBNnTpVEyZM0GuvvSav16vk5GRJUkZGhtq0aSN/lH+xBQAAAAAAqA2NqszZtGmTzj77bA0dOlTLli3T3Llzde+99+rAgQOaPn26rrzySl166aVasGCB9u3bp6KiIr333ntq2rSp7TodAAAAAAAAtanRrJmTn5+vKVOmaOLEiXrooYckSaeddppWrVqlXbt2yefz6brrrlO/fv30i1/8Qg8++KDS09O1d+9erVy5Ui1atKjjdwAAAAAAANCIyhyPx6Phw4dr9OjR4ddmz56tt956S3v37tXhw4fVo0cP/eEPf1BeXp5yc3NlWZbOOOMMtW/fvg5nDgAAAAAA8F+NpszJzMzU1KlTlZ6eLklatmyZZs6cqWXLluncc8/Vhg0bdMcdd2jZsmWaNWuWunXrVsczBgAAAAAAqKzRlDmSwkWOJA0ePFiffvqp+vfvL0n66U9/qtatWysnJychxzZN03bdnUAg4DiWnLyh5qZpOo6NtD3nEXmd5MFg3R4/jucRAAAA3K9RlTnf1759+/DtU6FQSCUlJTIMQ717907I8QzDkGEYjnm08eQ1zC1LhtPTyCzLebzHU7/fn4vzUCjkOC7SfjiPyOssj/b3SJSnHtaX8wgAAADu12jLnO/zer2aM2eOPvroI/3+97+v6+kAAFA3Du+SggciZ/lba3cuAAAAsNXoy5wXXnhBq1ev1rJly7Rq1Sp16dKlrqcEAEDtO7xLevx0qdThlrJkn+TLrL05AQAAIKJGX+b06NFDy5cv15o1a3TaaafV9XQAAKgbwQPHipxLn5Rado28jS9Tysiq3XkBAACgkkZf5vTs2VNLly5VcnJyXU8FAIC617Kr1K5vXc8CAAAADiI/FqaRocgBAAAAAABuQZkDAAAAAADgIo3+NqvaYpqmvN7I3VkgEHAcS17HedBhMdDaOH4Dzk3TdBwbaXvOI3JX5gn8e6S65xEAAADcjzKnlhiGIcMwHPNo48kTlHs80cf7/Yk7fiPOQ6GQ47hI++E8aqS5ZTmfh9HyWI8fLY/290hV/p6ppfMIAAAA7sdtVgAAAAAAAC5CmQMAAAAAAOAilDkAAAAAAAAuQpkDAAAAAADgIpQ5AAAAAAAALkKZAwAAAAAA4CI8mryWmKYprzdydxYIBBzHkpM31Nw0TcexkbbnPCKvUR4M1u3x69F5BAAAAPercpnzyiuvVHvnw4YNU1paWrXHNUSGYcgwDMc82nhyG5Ylw+9P3HjLch7v8dTvz6ce56FQyHFcpP1wHrk0r8p5lujzMJbjq/5+vtU9jwAAAOB+VS5zLr744mrt2OPxaNu2berUqVN15wQAAAAAAAAb1VozZ9++fQqFQlX64/P5EjVnAAAAAACARqvKZc6kSZOqdcvUxIkT1axZsxpNCgAAAAAAAJFV+TarxYsXV2vH8+fPr/ZkAAAAAAAA4IxHkwMAAAAAALhI3MqcQCCgdevWKT8/P167BAAAAAAAwA9U+TYrJ3PmzNHq1at1+umna8uWLTIMQ48//jiPJf8e0zTl9UbuzgKBgONYcvKGmpum6Tg20vacR+R1kgeDdXv8OJ5HAAAAcL+Yy5zFixfr4MGDWrlyZfi1F154Qb/85S+1cOHCWHffYBiGIcMwHPNo4xttblky/H7n3Gm8x5PY+SV6/w04D4VCjuMi7YfzKEF5Vc6zWPNEn0eJPH49Ps+rex4BAADA/WK+zer555/X9OnTJUlXX3219u/frzFjxuiTTz6JeXIAAAAAAACoKOYyJzk5WSUlJZKkn/zkJ/L5fJL4l0IAAAAAAIBEiLnMufbaazVt2jSVl5frmmuuUXp6uh599FGdd9558ZgfAAAAAAAAvifmNXNGjhypQCCgoUOHqn379tq7d69+/OMfa/bs2fGYHwAAAAAAAL4nLk+zmjBhgiZMmKCDBw+qRYsW8ng88dgtAAAAAAAAfqBat1nNnj1bb7zxhvbv3x8xP+GEEyhyAAAAAAAAEqhaV+bcc8894bKmTZs26t+/vwYMGBD+vyeddFJCJtkQmKYprzdydxYIBBzHktfzPBis2+O7ODdN03FspO05j8gTkrv4PK7ueQQAAAD3q1aZM3DgQO3du1dXXXWVWrZsqZycHK1YsUJz5sxReXm5WrVqpf79++uNN95I1HxdyzAMGYbhmEcb32BzKyTD73PODX9sx/fHON4p93jq9+dbj/PqPvWO86gO82g/51HPAyv6eex0nobKnf+eUKLnX3+/Pzw9EgAAoPGpVpnz8ccf66mnntLdd9+tgQMH6g9/+IM6d+6s4uJirV+/Xjk5OVq3bl2i5goAAAAAANDoVfvR5JMnT9bWrVvVrVs39e/fXzNmzFB5ebkGDRqkKVOm6IknnkjEPAEAAAAAAKAalDnSsUu9H3zwQX366af6/PPPdeqpp+pvf/tbvOcGAAAAAACAH6hRmSNJZWVlKi4u1mWXXaaTTz5ZV111lQ4ePBjPuQEAAAAAAOAHqrVmzv33368NGzZow4YN2rx5s1JTU9W7d2+dfvrpuuGGG9S8efNEzRMAAAAAAACqZplz9913q0OHDpo0aZIuu+wyde3aNVHzAgAAAAAAQATVKnPOPPNMrV+/XrNmzdLDDz+s3r17q3///uE/2dnZSkpKStRcXc00TXm9ke9qCwQCjmPJo+XBOj4+eU1z0zQdx0banvOooebRzuMoebAwSt5w/56o7nkEAAAA96tWmbN69WpJ0rZt2/TZZ58pJydHOTk5euaZZ3T48GGlpKSoV69eWrt2bUIm62aGYcgwDMc82njX5pYlw++3z0PlMvw+h717qnB8h/1Hm5+nKvsnT0QeCoUcx0XaD+dRTfOQ83mW6P1HPc9jPI+9SY32PK/ueQQAAAD3q1aZc1yXLl3UpUsXjR8/PvzaV199pU8//VTr1q2L2+QAAAAAAABQUZXLnLy8PGVnZ9ve4tCxY0d17NhRY8aMkSRt3LhR3bp1U5MmNeqLAAAAAAAAEEGVH03er18/HThwoMo7Hjx4sL7++usaTQoAAAAAAACRVfmyGcuy9Nvf/lY+n/OaB8eVlJTUeFIAAAAAAACIrMplzllnnaUtW7ZUeceDBw9WWlpajSYFAAAAAACAyKpc5rz77rsJnAYAAAAAAACqgtWJa4lpmraLRwcCAcexjT4PFsa4/2CM48kTlZum6Tg20vacR4nK6/g8CUY7fpQ86vi6/nzrz3kEAAAA96PMqSWGYcgwDMc82vh6m1uWDL/fPg+Vy/A733LnuH+PN0ruiTL/aLmc5x9tfuQ1zkOhkOO4SPtptOdRoo8f9TyxZBgO87NCzvO3rBjP81j/Hqjn3/8Y8uqeRwAAAHC/Kj/NCgAAAAAAAHWPMgcAAAAAAMBFKHMAAAAAAABcJC5r5oRCIS1dulTvv/++PB6PhgwZogkTJigpKSkeuwcAAG6Rv9U+82VKGVm1NxcAAIAGKi5lzjXXXKOSkhKNHz9eHo9Hzz//vP75z3/qqaeeisfuAQBAfefLlJJ90orr7LdJ9kk3r6XQAQAAiFFcypyPP/5YmzZtCn994YUXqkePHvHYNQAAcIOMrGNFTfBA5Dx/67GiJ3iAMgcAACBGcSlzevXqpXXr1qlfv36SpPXr16tPnz7x2HWDYZqmvN7ISxQFAgHHsQ0+DxbW7fHJ6yw3TdNxbKTtOY/cmged82h/DwSjjK/z9+ee8yihMrIoagAAAGpBXMqczZs3a+DAgTr11FMlSdu2bVN2drYGDhwoj8ejtWvXxuMwrmYYhgzDcMyjja+3eahcht9nn1sh5/Eeb+zz8/sdjm/Ftn+Pp35//i7OQ6GQ47hI+2mw51HUn1OrCueZw3kQ9fih6PuP5TyLdp57kzgPa+k8AgAAgPvFpcx5/vnnlZaWFo9dAQAAAAAAwEHMZY5lWbr00ksrrJkDAAAAAACAxIi8+EQ1eDwe9enTRxs3bozHfAAAAAAAAOAgLrdZbdy4Uf369VPXrl3l8/lkWRZr5QAAAAAAACRAXMqcV199NR67AQAAAAAAQBRxKXPat28fj90AAAAAAAAgipjKnCuuuEJLliwJP4L8OG6zqsw0TXm9kZcoCgQCjmPrR27Z58FgbPsPFsY2PtF5rO+P3DYzTdNxbKTt3X0e1ec81p/zKOOjneecZzXOq3seAQAAwP1iKnMefPBBSdLy5cvjMpmGzDAMGYbhmEcbX2d5qFyGz+HR86FyGYbfYe+Wc+7xOh/f44ky/yj7V5T3F23/UY8fZf+WJcMfw/wacB4KhRzHRdpPvT2Pon2fo+Whchl+n8P4UGw/R5Ylw+90Hpc555blPD+vV4aR7pAnJfY8a8R5dc8jAAAAuF9MZU7btm0lHbvN6oMPPtCOHTtUXl4ezq+88srYZgcAAAAAAIAK4rJmzmWXXaZ9+/apX79+SkpKkqQKt10BAIB64PAuKXggcpa/tXbnAgAAgBqLS5mTm5urTZs2xWNXAAAgUR4/XSp1WJ8o2Sf5MmtvPgAAAKiRuJQ5p59+urZs2aJu3brFY3cAACARSoPSpU9KLbtGzn2ZUkZW7c4JAAAA1RZTmXP8KVYlJSXq3bu3unfvrpSUFJ5mBQBAfdWyq9Sub13PAgAAADGIqczhKVYAAAAAAAC1K6Yyp3379pKkadOm6e6771ZGRoYk6dChQ3rggQd0//33xzzBhsI0TXm93ohZIBBwHJv43JQsK8p4p9xh/YVwbr8gdiBYGGV8tPlX5fgOeTDa+AR//nV9/DrMTdN0HBtp+/p7HsWax/pzUJXxMZzH0X5Oi4rkeJ7X+efbcPPqnkcAAABwv7ismbNq1So9+OCD4a9btGiht956y5Vlzvbt23Xo0CENHDiwRuOLi4tVXFwc/rqgoECSZBiGDMOwHeeUJTwvL5GRluyQp8jwpdrnliXD77fPPUnOx/d4o+Qe59yyErt/Jfjzr+vj12EeCoUivl4vz6NoP+fRfg6jHr8q46Mc35dmn5eXykhLsc9D5TL8DuO9TWSkp9vnSWa9/Tlr6LndeQQAAICGK/I/cVdTKBTS0aNHw18XFBSotLQ0HruuVevXr9eAAQO0fv36Gu9j7ty5at68efhPVhYLSQLVxXkEAAAAAPbiUub84he/0JAhQzRnzhzdd999OvPMM3XbbbfFY9e1Jjc3Vz/5yU907bXX6rrrrqvxfmbMmKEjR46E/+zatSuOswQaB84jAAAAALAXl9usrr76ap1++ul65513JEnPPvusevbsGY9d14pt27Zp0KBBuuOOOzR79myVlpbqH//4h/bt26fWrVtr6NCh8jvdXvE9KSkpSklxuJUBQFScRwAAAABgLy5ljiRlZ2crOztbu3fvVmGh84K29UlZWZkee+wxGYahvn37SpIuvvhi7d69WwUFBfr66681atQozZgxQ/369avbyQIAAAAAgEav2mXO5MmTdejQIZ188sk66aSTdPLJJ1f4k5qaqjvuuEPLli1LxHzjrkmTJpo6daqKior0yCOP6Pbbb1evXr30/PPPq3379tq0aZNGjhypefPm6emnn67r6QIAAAAAgEau2mXOn/70J40ZM0bLli2Tx+PRwYMHJUkez38fSXvmmWfGb4YJUl5erqSkJElSly5dNG3aNM2aNUuZmZl65JFH1K1bN0nSgAEDNH/+fI0cOVK//vWv1bVr17qcNgAAAAAAaOSqXeY0a9ZMb775pn7zm99oyJAhOuecc7R79+7wnz179uiiiy5KxFzjZuvWrXr11Vc1YcIEtW3bVpLUuXNnzZ49W5s2bVKnTp0kSZZlyePxqKSkRN26ddOJJ55Y42OapimvN/J604FAwHFsfHLLIQ9KlkMeLJS+V9ZFzOWQFxZVYX4JzIPBuj0+uW1mmqbj2Ejb1+15VNe503lchfPcaf/RzvOiEue8Xnw+jTOv7nkEAAAA96vRmjler1dz5szRwoULtWjRIk2dOlVdunSJ99wSYvv27Ro8eLAOHTqkAwcO6Pbbb1fLli0lSaeccoqysrLCVxkd/7//+te/1L59e9tfIqvCMAwZhuGYRxtf47y8TEZaqn1eWizDl2afezzO+/d4nXOvV4bhtIC0FSWXjCgLUDvPL8r8o423rNiOT26bh0Ihx3GR9lNn55EViu3n2ArJ8DucZ6FSGWkOiz6HymX4ffZ5eZmMtKYOx/c7zy+pqYz0dIfcrLc/R409r+55BAAAAPeLaQHkG264QatWrdJdd92lOXPmhG9bqq8CgYDmzp2rESNGaODAgZo6darKyso0bdq0cKHzfRs3btRzzz2nJ554Qu+//76aNWtWB7MGAAAAAAD4r5ifZjVs2DBlZWVpypQpevDBB5WRkRGHaSWG1+vVgAEDlJmZqXHjxqlly5YaP368JIULneNX4+zYsUN33HGHtm7dqtWrV6tXr151OXUAAAAAAABJcXo0effu3XX//ffrlltu0cyZM3XqqafGY7dxl5aWpkmTJsn/n1tmxo4dK8uydNlll8myLE2fPl2ZmZkqLy+X3+/X/Pnz5fV6dcopp9TxzAEAAAAAAI6JucwpKyvTqlWrtHjxYr300ktauXKl1q1bp5NOOike84u740VOeXm5vF6vxo0bJ8uyNGHCBHk8Hv3yl7/Uww8/rK+++krPPfecUlMd1poBAAAAAACoZTUqc7Zv36533nlHq1at0ttvv63Dhw+rbdu2mj59uq6//vp6W+R8X1JSkizLUigU0vjx4+XxeHTFFVfolVde0b///W+tXbuWIgcAAAAAANQ71S5zbr75Zq1YsUL79+9XamqqpkyZossuu0wDBgwIrzfjFsfna1mWxo0bpyeeeELr169XTk4Oa+QAAAAAAIB6qdplzuOPP67HH39cu3bt0gcffKAPPvhA8+bNU7du3TR58mRlZWUlYp4J4/F4VF5erjvvvFPvvPOO1q9fn5AixzRN20ebBwIBx7FVyy37PBgtDzrvv7BYkn1RFwgWRsmj7D/q+4t1fILzmN9f481N03QcG2n7xJ5HTnk8vs8O52Eg6BT/5+cslvO4SHIo3ANFxc55Pf45aux5dc8jAAAAuF+N18zJysrS+PHjw0+D+uKLL7Ro0SIVFBRo5syZat68edwmWRt69uypnJwc9e7dOyH7NwxDhmE45tHG2wqVy/Cl2eelxTLSmtrnlk/Gf9YSisib5Hx8jzf6/P0+h9QTZXyU3BNtfIyfb7S8ro/v4jwUCjmOi7SfhJ1H0fJQufN5EiqXkZZin5eXOuehchl+h/M4VCojxemvbOfPRskpMox0+zzJrLc/J+TOeXXPIwAAALhf5H/iroHTTjtN9957r84991yNGDEiXrutFUlJSbr66qvVt2/fup4KAAAAAACAo7g8mvz7fvOb32jPnj3x3m3CuW29HwAAAAAA0DjFvcyZN2+eTjjhhHjvFgAANAT5W+0zX6aU4a619wAAAOpC3Mucs846K967BAAAbufLlJJ90orr7LdJ9kk3r6XQAQAAiCLuZQ4AAEAlGVnHiprggch5/tZjRU/wAGUOAABAFJQ5AACgdmRkUdQAAADEAWVOLTFNU15v5IeHBQIBx7HRc1Oy7B9NGwgGJVkOeaEk+wWgA4VFznmwMMr8osw/1vHkrs1N03QcG2n7hJ5Hjnkw+v6dzsNAQLKczsMo46OdJ0VlcjxPi4qd83r8c0Ie3/MIAAAA7keZU0sMw5BhGI65LcuS4ffb58GDMjwOv+ilpsjwpTrMziPD77OPvUnO8/N6nXNPjLmifD6S8+dTlfHkdZKHQvblhd1+EnYehcpl+NLs8/JS5/OotEhGSpLD+GQZqQ55qUdGk3L73O93fn/JZTLS0+3zJLPe/hyQx5ZX9zwCAACA+0X+J24AAAAAAADUS5Q5AAAAAAAALkKZAwAAAAAA4CKUOQAAAAAAAC5CmQMAAAAAAOAilDkAAAAAAAAuwqPJa4lpmvJ6I3dngUDAcWzAPCpZ9o8sDgSLJcv+0bSBEkvyeOzzwqIoebHz/IIOj0WXFAgEo+RR3n+sebCOj09um5mm6Tg20vY1Po+i5lX4OXE6z4LBKHlAcniE9LHzzOE8LC2RPPb9e6Co1Pk8rsc/B+S1ex4BAADA/ShzaolhGDIMwzG3FfhORqjMPk9LlpHW1D5PbiLD8NvnScnOx/cGnHNPknMuT5Q8yvv3RBkfax7t+OQJy0MO5YbdfmyPZVky/A4/5+VlMnyp9nmo1DkvLZKRkmSfF3tkJDu8n6QyGR6HX9jTM2Skp9vnRWXOuWnW2+8zeWLz6p5HAAAAcD9uswIAAAAAAHARyhwAAAAAAAAXocwBAAAAAABwEcocAAAAAAAAF6HMAQAAAAAAcBGeZgUAAOqP/K32mS9TysiqvbkAAADUU5Q5tcQ0TXm9kS+ECpgFUqjcdmygsFgK2X+rAsUOjy2XFCgtkTweh/2XSHLIg0Hn/QeLnPOAw+OYyRt1bpqm49hI29ueR9HmEQxIsuzzQECynHJTCtmfa4FAUAql2OfFpZLHIQ8VSl77R58Hikqdz+N6/H0mr1/nUb3ly5SSfdKK6+y3SfZJN6+l0AEAAI0eZU4tMQxDhmFEDo/uk1FaaD/YUyJDJfa5r4UMf5p9XuqxP7YkJRU65x5Fyb0yDL99rijjo+WeKPOPdf/kdZaHQiHHcZH2Y3usULkMv89+cFmJjDT7MkXFpoykUvvcMmWUFjjkZTJKj9jnGZ1kNGtunxeWykhPt89Ns95+H8nrNq/ueVRvZWQdK2qCByLn+VuPFT3BA5Q5AACg0aPMAQAA9UNGFkUNAABAFbAAMgAAAAAAgItQ5gAAAAAAALgIZQ4AAAAAAICLUOYAAAAAAAC4CGUOAAAAAACAi/A0q1piFhyRV1bELBAISmVJtmMDhaWSku1zKyh57Hu5QGm5JI/D/ouc80DQOQ8GbbP/jnfKA+SNNDdN03FspO293sg/69HncVQKlUXJm9rnhUVSucN5Wlouee0ffR4IFkpJDudxUYnkcToP6+/3kdxd5xEAAADcjzKnlhhlh2SU2fwiae6QcfiA/eATB8hIs/8lUTJk+NPs4+KQc+5NkmEYDvuXc+7xRhnviXH/UcZblgy/v+b7J6+zPBQKOY6rtB+/3/57XVooI8W+bFHIlFFyxD4v2CXj8GGno8sodPil+ZRzZTRvYZ8XlshIT7fPk8x6+30ir995dc8jAAAAuB9lDgAAcI/8rfaZL1PKyKq9uQAAANQRyhwAAFD/+TKlZJ+04jr7bZJ90s1rKXQAAECDR5kDAADqv4ysY0VN0Oa25Pytx4qe4AHKHAAA0OBR5gAAAHfIyKKoAQAAEI8mBwAAAAAAcBXKHAAAAAAAABfhNqtaYh4+IG95ccQsUFIuNWlmOzYQLJKSmtrnZUHJY9/LBYpLJY/HPi+MPK9wHgg658HCKOMDseXBKMdP9HjyhOWm6fCob5vtvd7IP+uBo0elslL7eRw9JCU7nQdFUhOH80xNpJRM+zwQkJqk2OdFxc7nYT3+PpHX77y65xEAAADcjzKnlhjPj5SRYvOL3PnzZXgt+8HNT5Dh99nnpUkyDIe8SYkMw2+fe5vIMAz73OONMfckNpcSO548YXkoFHIcV2k/fp/9ubD3GxmFDr/U5r0gY9+/7PMOw2UEv7bPhz0qo2Vb+zxYKMNIt8+TzHr7fSB3d17d8wgAAADux21WAAAAAAAALkKZAwAAAAAA4CLcZgUAABqO/K32mS+TR5sDAIAGgTIHAAC4ny9TSvZJK66z3ybZJ928lkIHAAC4HmUOAABwv4ysY0VN8EDkPH/rsaIneIAyBwAAuB5lDgAAaBgysqIXNdyGBQAAGgDKnFpithogb1rkjztQXC6ltLAdGwgEJa/9typQIslj89hzSYGiYuc8WGSbHcsLnfNAgJy8RrlpOjxKPNL2Rw7Ja5VFPo55VPKU2s8jpY3U5sf2edMTpTSH8zAYkEz79xIoLJLkcJ7V4+8Dubvz6p5HjRa3YQEAgAaEMqeWGIGdMsptHh7W8mQZht9+cNPmMgyffV4iGYZhnzdp6px7vM7H93icc0U5viTDH+N48gaZh0Ihx3GV9rPvIxmmzbmQ87SM3e/YDx79d+ef4xY9ZDQ/wT43Tef36U2qt58zecPOq3seNVrchgUAABoQyhwAANA4VOU2LAAAABegzAEAADiONXUAAIALUOYAAACwpg4AAHARyhwAAICqrqnz9Uf223DlDqCnnnpKV111lb766it16NChrqcDAA0WZQ4AAIDkvKYOV+4AAIB6hDIHAAAgGp6GBQAA6hHKnFpinjhA3rTkiFmgqEhKbW47NlBeKHntv1WBknLJY/PYc0mBwmJJHvs8EHTOg0Hb7L/jnfKAcx51/1HGk7s2N03TcWyl7UtC8pZEfgxzIL2j1CHFfh6llhRKtc8DASmpqXPugJy8rvLqnkeIQVWehsUCygAAoBZQ5tQS4+QeMvw2v0hmniTD73MaLcPw28fF5TIMwz5PCjrnHm+Cc09suUTeQPNQKHIxY7uf92bJSE2KHF6xSsaJp9gPNk3neUbLVX8/R/LGnVf3PEKCVPU2rHFLJF/Lmh+DMgguc++992rWrFnatm2bZs+erZdfflmWZenSSy/V448/Lp+v8n8Db926Vd26dYu4v+bNm+vw4cMJnjUA1H+UOQAAALGKdhtWMF96/gpp6aiaH4M1eeBiY8eOVceOHTV37lzl5OToL3/5i0488UQ98MADlbZt1qyZlixZUuG1zZs367777tPPfvaz2poyANRrlDkAAADxEO02LKeyJ5qqPE0rGq7sQR3q16+fFi1aFP76wIEDWrRoUcQyp02bNpo4cWL46y+//FJ33XWXhgwZoqeeeqo2pgsA9R5lDgAAQG2oypo7dqpyG1c00W7zouxBAt14440Vvj7zzDP10ksvqaCgQM2aNbMdt2vXLv3P//yPTjrpJL3++usRb8sCgMaIMgcAAKC+i3YbVzRVuc2LNX2QQKecUnFduxYtWkiSDh06ZFvm7Nu3T0OHDlXz5s31j3/8I7zdM888oxtuuKHCtqFQSIWFhVq9erXOOuus8OvdunWT3+9XTk5Ohe07dOig7777Tt9++638/mNrUwaDQbVu3VqZmZnasWOHJOm9997TtGnT9MUXX6hJkybq06ePFi1apI4dO9b8wwCAOKDMAQAAcINYruyRamdNn1jKoFhRJtVrSUmRH2BgWVbE1w8cOKBzzz1XSUlJWrVqlU444YRwdvnll+vyyy+vsP3UqVOVl5enn/zkJ+HX1q5dqz179qi4uFhffPGFTjvttApjTjrpJL388svhff3f//2f2rZtq5KSEknSkSNHNHLkSC1atEgXX3yxgsGgVq1aZfteAKA2UeYAAAA0Bolc0yceZVCsYi2T2vWN63RQc0eOHNHPfvYzFRYW6r333tOJJ57ouP2KFSv0/PPPa/369RWKlqVLl2rkyJE6dOiQlixZojlz5lQYd9lll+mZZ54JlzlLly7V5ZdfrsWLF0s69lStlJQUXXrppZKOPVXwkksuiedbBYAao8xJsOP/2rDf00pBT1rEbYoOB2SWRP5XCUkqUqnMwiL7vLRcZiBonxcXywwE7POiaHlR9Nw0ycmrnR89elSS/b/KHRc+jzIHK5jWNPJx8vNlhiJnsc6TnLw+59U5j44WW9JRUyoocNwWjZS3uWQ0r9lYo5N0xT+lwoPxnVNVBQ9IK66XFl1a833M2K309HR5PJ74zQvVZpqmzjvvPH377bdas2aNTjrpJMftd+zYoWuvvVZLliypsG1ZWZmef/55/eUvf9Hhw4f129/+Vvfdd1+F7+///M//6K9//au+++47SVJeXp7uvvvucJnTtWtXlZSU6Nprr9W4ceM0aNAgx/V9AKA2UeYk2PH/yO56wS11PBOg/jp69KiaN7f/BSJ8Hk1Z6rCXv8Z5VoC7VOU8an7/Uen+M2txVoCL3N9cR44c4Zf1OnbFFVfoo48+0m9/+1u9//77ev/998NZdna2+vbtG/66tLRU48eP19VXX60LLrigwn7eeustlZSU6Oc//7mKi4t1/fXXa82aNRXW00lKStLo0aP1/PPPS5JGjRpV4cqe5s2b67333tMDDzygK664QocOHdKYMWM0f/58paenJ+gTAICqocxJsHbt2mnXrl0N9l96CgoKlJWVpV27dvEfPw74nCKzLEtHjx5Vu3btHLdr6OdRXeBnMjHq4nOt6nmUnp6uI0eO1MqcGhrOl/iqz58nv6DXLcuy9Pbbb0uSfv/731fKH3/88Qplzt133y3LsjR37txK2y5dulQXX3yxmjZtqqZNm+r888/X0qVLK5Q50rH1d2699VZZlqU//elPKi8vr5BnZ2dryZIlkqTPPvtMY8aM0X333af7778/1rcLADHxWNGuywYcFBQUqHlz/iUrGj4n1Df8TCYGn2vDxPc1vvg8EQ9vvPGGJk6cqM8++6zSk6VM01Tr1q2VlJQUfpR5IBBQcnKy9u7dq5SUFHXo0EFLly7VkCFD1L17d0nS5s2b9a9//Uvjx48PP83qh+644w5t3rxZr732WkLfHwBE463rCQAAAABAVX3zzTeaNGmSnnzyyYiPCF+xYoVatGihLVu2aP369Vq/fr02b96sJk2a6PXXX4+4/YoVKyq9vnnzZj366KPas2ePpGMLIr/66qs6/fTT4/+mAKCaKHMAAAAAuMaiRYuUn5+vSZMmyTCMCn+eeeYZLV26VNdee63atm2rNm3aqE2bNjrppJN09dVXa+nSyuvv9ejRQz169Kj0enp6uj788EMNGDBAfr9f5557ri644AJNnz69Nt4m8P/Zu/cwyaryXvzfvXbda3X3wDS3gWG4yCigMMhFQBAxGsGI6ERQzCDgXY+HBPCYH1Gj3BP0qFFRHBAxIogKmMRoIiASjRIvQVA5ikaBQUCZgZme2re67P37o52WsWu9b9Xsqq7unu/neXgenbfW2mvvWu9au1ZX7UUk4s+sKJckSXDZZZfh/PPPR7lcHnVz5i1eJ5pv2CeHg9d1ceL7Oli8nkRERPlxMYeIiIiIiIiIaAHhz6yIiIiIiIiIiBYQLuYQERERERERES0ghVE3YLFL0xSPPPIIxsbG4HneqJtDNK9kWYbNmzdj2bJlMMa9tsw8InJjHhHl12seERERzRdczBmyRx55BMuXLx91M4jmtXXr1mGPPfZwxplHRDrmEVF+Wh4RERHNF1zMGbKxsTEAwLpzLMbL3f8S2jj6fFg0nHU0lj0Xtuj+K2ojrcCW3M+xbvg7wFaKQvkibK3ijgcRbNW920Qj6cDWa+54mMDWq+54FMNa6443QlhbF+KbYetSPJDLh9rxG3J882al/qiH8kI8CJV4kK/96vlNKeWV4wvtm5qawvLly2fyxGUmjz7wfIxXuw9bDX8CdsO97nZU9oB9/Afu+PIXwG68zx0/6HWw6SZ3fPLZsIW2O17aFdYxBgBAw7OwFd8db/uwFSEPm5mcx3EKa4U83TwFWy2540kml28Ecp43Arl9UUuuP27BWnc/aUxtkvNQzSMlruaBkkda/Q1lHBDybGpqCsv33LP3PPr3j2Hc8V41fvMz2E0/d7cjCGF/+1/u+PMugPUTd3yHA2ELHXfcG4d1d8Pf54n71qXRKcn9LElha0IeRS05Hjblfp505Pdx0ybYujTfhsp82eyhH0rzUTKA+SBHnuWYL3pqX0/zdb48IiIimi+4mDNkW77KPl72nIs5pl6BhftDoLF12JL7Q6BJq7Cl1B33rfghzXSK4s2jMQXx5tYkHXExxRRicbHHFEvizZcxBfHm0RhPPr7x5fIF7fhGjnueUn+xh/JC3C8ocT9f+9Xzg1JeOb7SPgDqTz5m8qhawHi1+8Kk8YviYoipFsTFFFMtwsZC+XoFNnV/SDW2Ji7mmLKVj698SDVtX1xUNUkmfkg0pVTup8iUPFfKGyPnuTGwNWGcKTbl+kvyYo5BJpdX80iJq3mg5JFWv1HGASXPgD7yqF7FuGPhzNTKsC334r9JlTyqV2F9IW7r4mKOMVb844SaJ52ynAdxKscLTSWeyP1cWcwxWSrPt76vzJfyYs50P5TmI3kxp7f5IEee5Zwv8s9ncp4Beh4RERHNF/xRMBERERERERHRAsLFHCIiIiIiIiKiBYSLOURERERERERECwgXc4iIiIiIiIiIFhA+AHmONHY+HMaxC0/QNkB1Z2fZIGoCvvuBg0GrDRj3A46DZgwID/QLmi0AQjyK5XjS7KG8WxAlcvkglMtr8TCUzz+MlPoDpX4lHuWtXykfatdHqV+N57z+Qv2NhnsXt66vH3sajOMhvUEYAzse7G5H2wC7He2OF5YAk6uE8h5Q3sUdb6WA736waRA3AU+Ip7Ecb7Yhrb8HSVvu50lLjmt5qpVX+6kSj5py/bEyzmh5oOZJCMD94N3841DOcUCov+88Qh0G3R+yG3gWGHuaux3NR4Fdj3LHWylQ2tEdj1uAsNtU0GoDnjCfdZqAJ+RBK1LyQMmTKJa62fQ4I/bTlrswtvRDoZ/1kid58kDL87zzxdDno+HF+80jIiKiUeNizhyxCGBdl7tWhYVwA1ItwfruXXJQ3VncehwtwFaEvV5NCuvexAYwVtwtCkEo7r4BT95dAz3s0iJtWQxllxl4Sv09HV9oP7Tjy7ttIU3l62eMuIsPALl+rX1aPMuU8sruIMLuIWnq3oWtG5v8FtZ39HVTgX3SvaUydn8ebCDcrNfqsJsfccerVdhss1Dewhal8ynCFprusL+DnMflmtwPQ3mXHQTy1uHwlH4UxnoeaOOA2M8jZZyQd8UDeujnUvmsI+4WBniw0pbFWaacv7zrHbK0hzzuHk9T9+5QXetBAOtaUKgUYKUPxGMTsJ3fuOOVEmznSXe8vre4OyPSsribFQpj8vsUd8TdG1Gsyv3EL8rvo19S+mlD3BUOmVXmo5x5ou3W5Mu7K0LZFQ7oJc+U+U5YzEOWirveTeeJlMfZNs+HaSb0OyIionmIP7MiIiIiIiIiIlpAuJhDRERERERERLSAcDGHiIiIiIiIiGgB4WIOEREREREREdECwsUcIiIiIiIiIqIFhIs5REREREREREQLCLcmnyON8afD1Lpvlxq0AFQnnWWDpA347q04gzAGPPe6XJC0Abi3gg3ilhRG0Mzk8mEkx6MY8KTysVw+CN2N6+X40ja7vdSvls8ZD5V4pF2fIbcv9/m5r2+jIWwV3u31dk8Yx7a/QSMEdjjQ3Y6sAow9TYjXgImnu+MdHyjv5I4nbcC4tyQOOm3AuLdMDpoJYIQ8bgVQ80zYWjeIlDwJI2dsOh4rcS1Pe8hjcZzQ2qfFtTxX4lFTaZ9WPmf7BplHsDDoPqcEaREo7+ZuR6sBTDzDHW8beT6L24Bxbz0dtLU8UeY77X1qppDzIARc27ajhzxQ5xOlH+Sd7/LmSe75cADjQJ54juP3m0dERESjxsWcOWKnfgbbclzupx0P67fchetjsGVhtcWrwJaFL1kVxmHrFSHekuNxG7Zed8eNkeN+AdYKcc/AWvdiFZD1UN4K5SHHsxS2Jpy/Vn7YcePL1zfL5PKep5x/Ntz6hXiapu5yXdhN98M2i92D/hLYjfe5C9eXwm7+tTu++/Ngm013fIfdYIsdd9yvwhaEeH1H2Kqj7QDQNrAV94dYxC35+GkRtuIL7bN6P6xJeQg5T7Q8NIV8cXiwdaF9niePE1mq1K+MMyZR8kSp3+/h/MQ8cl/fNHUvPnRjvRDWNaU0N8Bu+pm78Ph+sMFGd3xiUu6n5Sqs0M1RXwpbdS+KIklhHX8YAQAUqrB192IRggi2It36VGFrQnloeaC8j4Dcj42v9ANf6ac9zIfS+WnjfdqBdSyo/76Cbe7HAPQ80cqr8cHNR0RERKPGxRwiIiIiIiLSbVwHhBvc8WWr5qwpRNs7LuYQERERERGRbOM64IojgJbwk8b3bZq79hBt57iYQ0RERERERLJww/RCzuqrgMmVs+Pr75/7NhFtx7iYQ0RERERERL2ZXMmfUxHNA9yanIiIiIiIiIhoAeFiDhERERERERHRAsKfWc2RxpIDYBzbqQbNFKiNO8sGcRPw3FuJBmkMeO6tToNmCHjurc2DOAGEnc+DuAnpBUEYAsLOuEEUu4MAgkB4iNqW+sV4NOT6c7Y/CPKVV89Pq3/Ice36CeUbjYZYdtbrx54G49gWN2h2gMlD3O1AHRh7mjve9oHyru54swP4Qh62O4Dn3no8iCI5D5vCds4AgiiBmIdJW6m/LdcfhpASebofCvVHiVx/3jzR+lk05HFAHcdy5nmOPOw7j2Bh0H3OCPxxYLzLcxC2xFEB6nu740kbKAjzUdwCPPfW3kFLyZO4Jc9XSQfyfBXJ81Ws9OO85QMlj9T5JoCep1L5nPONFs87349wPus3j4iIiEaNizlzxJam/+uqvgS2KHzQqpRgS6k77k/AVl2VAyj6sHX3zTNMQY57PmxNiKdt2IrQlbwqbN39IRgArLVC+3w57hlYWxfinlxeOz482JrU/pz1eybf+Wv1Z6nSfq19yvnluL5pKvTrbvWUMtiS44PMxArY5m/dhTsl2OA37ri/DDZ+zB3f5UA5TwtL3DkOAGkBtiCU94qwReFTohmT86jYgq27P0QjjGGr3RfCAADtppzHqMn1A/I4pPWTLJXPzxhYOybXXxfGAS1PtfYZXx5nkDPPtfJCvO88qhZgq46Fxx12hW357sJxGzYU8qxahvVb7nipAlsWVmO0PPAqcj8rGLkfpG3YqnB+fk3pp5DnQ0DOM7UfKPNNmir9HEqe9tLPc+SJ3+ihH+eYr7N0aPN9v3lEREQ0avyZFRERERERERHRAsLFHCIiIiIiIiKiBYSLOURERERERERECwgXc4iIiIiIiIiIFhAu5hARERERERERLSBczCEiIiIiIiIiWkC4NfkcaVSXwzi2Mw2aHaDg3ko0iNuAcW91GiQx4Lm3eg2aTcBzr9sFcQIIO8UGYeQOAgjCGFIFQdKU2xdEcvlIOX4QAHBvZau2PwjleKjEozhn/TnPLwiU+gdx/OHEG42GWHbW6yvLYKqOPEqLQHlXoR0hUNvLHc+KQHUPdzzpAL6Up00AQp62WoDn3lI5aDUBI+RpM9LzWKD201CJx0oeq3nSVI6vlVfOL3c/V8qr/Txv+TnMo7QEk3bvi0GnABSWutuRbQKqy93xJAUK7q21g6QFGPfW3kEnkft5U5vvMkh/p1LzoNmGOB/1Mh9I7Ytz5kHu+OjG++n4sPN02/Ow3zwiIiIaNS7mzBFrmrDGcYNn67BF94d1FKqwZWG1xa/BOj7gAgDKRVhrhfIN2Lr7QyoAWMdCFADAM7C27o6HsRyHUt7zYOtCPO3I7fd8+fzhyXFPiRut/kyp38jxLIWtuT8cAYC1Y0o8T/uV8lkmvz9C+TRNxXKz6il5sCVHLmRF2NZGd+ECYMPH3PGdVsJC+CAyvsR9bABoerC+8EGxuAS2Igy59SXydQ4iJU83wpZ9d9ir6Xkg1e8reewr44y3SRlHPKUfb5THuUzJs7z9vKe4lAeZfH3z5FEmzB/d6ikXYSvF7sFqEbYtfKitVWCbU0LlddiikNd+RZnPxuT3uenB1oV43OnhfRDG00YIW3UvugLK++wXlH4Y6POtWF6ZLzxPOX8lz4Ft7ocAcvVjAD3MhznnU+H4/c5HREREo8afWRERERERERERLSBczCEiIiIiIiIiWkC4mENEREREREREtIBwMYeIiIiIiIiIaAHhYg4RERERERER0QLCxRwiIiIiIiIiogWEW5PPkUZpEqbUfTvUIGkDvnur0CBpAaYsxGNI63JBqwnAvRVsEIbO2HQ8kuNRE/CE+iNhu+ZBHF8rrx0/ELaj7imuHH/Y5aMY4vurldeuX+7r4443GsIWyN1e7y+B8V15lALFndztaIWA3UeIAyhPuONRE/DcWxoHzQ7guYfUIG0BRor38j67t6AOQqWfJ23AE8aJXvJI2FE6iBK5vFp/glzjVKSME7nzdP6OA33nUcvAtLr3haBtAF/IgzQESju740kbcOTodP0twBPms0yZz5K2MzYdb8r9NIyUfqzFe5nvtH6co58PcTyePn7O+XbU802O+vvNIyIiolHjYs4cse0nYNuV7sGxZ8NWhLfCG5fjbQNbKbrjBR+2WnLHsxS25r75hufD2ro77oew1grxohz3jBw3vhwHlPIFOZ6lsHX3YppaP7x87dPKe0o87/UBYOvC+5tlenwb25+mqdiuP2bjx2B9Rx7VV8CmU+7Ck3vAZpvdcVOV45UirN9yx+vjsGXhy45mCawV+tnmKVhf+KBaKcNW3R+C1TwK4x76gdA+o4wDWp6lqVzeM/I4lKVKHijnD0/ux2lHPn8tD9U81toH5foMMI+wGTZzlKlWYT3hA7E3JufZ2BLYsrAaklbkPIGFrTtyHAC8BLYi5VkNtia9j9p8U1Lmu4aSB8p4rPUjtZ8o5bXxGEo/MwbWjsnlpTyCMt9o56/NN1r92Pb29ZtHREREo8bFHCIiIiIiIgI2rgPCDd1j6++f27YQkYiLOURERERERNu7jeuAK44AWsI3NIs1oLZ07tpERE5czCEiIiIiItrehRumF3JWXwVMruz+mtpSYMnyuW0XEXXFxRwiIiIiIiKaNrkSWLZq1K0gIgW3JiciIiIiIiIiWkC4mENEREREREREtIDwZ1ZzpFHaBabUfdvdIEoA4946PEhjwHNv2RskTQDurWCDphKPYjkeJ1IYQRjJ5cPIXRhAEARKXHgIWy9x9fh5y2vtzxkPtfMf8vGH2P5GoyGWnfX64iRM0ZFHLQ8oLXG3owWgNC7Hi+4teYO4BXjuLZODdhPw3FuHB2kIGPf6eRA1nTGghzzWxoEoluvX8kArr8VDrZ/kzNPc/XjU48C2j3N951Fahkm799WgFQKOHAOAoB0DBSlPEsBzbw0etFqAJ8x3nRDwpDyJ5fkoSQFPyoMEYp4EIYDMHdfG49zzTd5+3kt56fyU+4GFPl8NcD4iIiIaNS7mzBFb8mDLjhukahW2lLoLmypspeiOF2qwdfeHTMQd2Lr75hx+AdZaIR7C2ro77vlyeQC2Jhw/bcPWhPZnqRLvKO3z5HiWyu33jH5+YjzroX3S8ZW4evwhx3tpX737+aedjlhuVj2FDmzRUaZsYT3hg0RWluOmCgvhZr5ahi25PwShXIWt+O44anI/9nw5T4NIjhulPKDnmdRPjTJOeMb5PgMA0g6sdX/Ih1HyLEthq+7FMmSZcnxlHIHSz40yzmXZNudBT+WFcShNhfnD0Q7nsdKOnCelAiw2u+OVsjyflcdhK8KtR1pW+nlBnu+Kynygvo9KP1HnA09+n9HLeCnkSZbqca2fSu3zjFw/epgvxPNXymvjUN75UCjfbx4RERGNGn9mRURERERERES0gHAxh4iIiIiIiIhoAeFiDhERERERERHRAsLFHCIiIiIiIiKiBYSLOURERERERERECwgXc4iIiIiIiIiIFhBuTT5HGmYcxnTf7jNImoDv3go0SBPAc6+7Bc024Dm2PQcQxC3AHf59XCgfRO7CAIJQiQeBEg8hNTAIBxHXji/Ec59fL+c/zPqHHFevr7t8oyFsBd7t9f4EjCNXglYHKAp51AZQdG95G7RToODe0jaI24Bxb5kcdJqAcW9pHLQjwHNvXR7EiTMG9NAP40TOY6183rjaj5V4FCvHz5mnWvm8xx96ng0wj1oeTKt7XwlaKVCQ+nGo5EkLcMx1ABB0YsAT4q1Inu8ipZ8nTWcMAIJQeZ/VfqrkaRAAyITjDzmP1PlMO7+87cs7X4xuPu43j4iIiEaNizlzxJoY1jhuQKtV2LLwJanCEljrvvlFI4KtFoXyJVjrvvmGH8Na94dcwFPigK27P+Qiy+Tynie3zxTkuFbeM8ONZylsXXh/kOWrH5jfce36Z+7zT9NUPO6sdtSqsDVHX4vbsO0n3YUry2CN8EHMlGA94YPCxCRsreyONzPYSskd7xTlPDFG7kdpB7biXgyCqcrlswy25v6QDiBfec+DtWNyeWkc8AtyeSj9zC/m66e+Ms4AsHWh/UI/7+n4WhyZ8/1JO22h3Gy2XnO/1/EmWAgfiAseLIQPvUv2c+coACRtOU/aBTlP/JLcj4JQGY+1+SrV50MpD9KOfHx1vhnxfGb84c5Xaj/Xyg9vvux3PiIiIho1LuYQERERERFtDzauA8IN3WPr75/bthBRLlzMISIiIiIiWuw2rgOuOAJoCT9pLNaA2tK5axMRbTMu5hARERERES124YbphZzVVwGTK7u/prYUWLJ8bttFRNuEizlERERERETbi8mVwLJVo24FEeXErcmJiIiIiIiIiBYQLuYQERERERERES0g/JnVHGl4Fsbrvl1pEDcB497KNGiGgOdedwsiYTtlAEGrA3iObdEBBGEslw+Eh6QBCEIlrpZX2q/Fc9evtV/YpncQx9fKq8dfuPFGQ9jiuNvrswpM1n1b4aDdAArura2DpA2U3VuLB60OUHRvWRwEIWCEPExSOU+bLQBCHkYRkDnD03F38enzk46v9fMoVsaJUI5HMcTz6+X4Uvnc/TDnOJX3+EMcZ/rOoySFKXXfhjlopkDBvfV20I6Agnvr5yAIAc99axEkTYjvc7Mp97M4ccaAHq6zGo96yAOlfrF83vlo+50vhh3vN4+IiIhGjYs5c8R2NsF2mt2D9X1hqyV34awEWxbuHlGDrbtvvhF3YGvuD6nwDKytC8dPYWtC/Z6Bte6be8CT6ze+XF6tH3Jcrd+T42kHtu5ebFPL93J96sL1gXJ+WZav/AjjaSasXnSrp+jBlhy50PJhM+FG3i/Dppvc8cIYrCd80CqksOlmd3xsmdzPgxC2JuR5O4YtdtxxOyZf5yjpoZ8I/dgv5ssj48vnn3aUccDI7ctSuTwg50GW5svD3OOAEs+Rx2l/aQRbLcHWHAubDQMLIY8qZVgj/AHA78CmT7rjtZ1h68J8lGRyHICtSLcuNaUfZT3Mh1I/UeZLvwhr3YvKPc1nUvuRs59pcWjjuTKOqOXnbzxNuy9wEhERzVf8mRURERERERER0QLCxRwiIiIiIiIiogWEizlERERERERERAsIF3OIiIiIiIiIiBYQLuYQERERERERES0gXMwhIiIiIiIiIlpAuDX5HGn4S2D87tt5BlEMGPdbEXRiSOtuQdICPPfW5UHSlONRAkCIB6Ez9ofyQjwQtrkFEETCdtC9lNfiodL+vOXDBd7+vO3LEW80GmLZWa/PSjBp9+29g04E+BPudrQBFN1bCgdtDyi5t7QNmm3AVNzxTgPwhDwNQkDYil3No1ZLrl8rH0YQ8zzWyg+5H+YeB/L2cy3PtfYL23X3dHzt+g0wj5opTNJ9G+agBaDg3no6aLWBkntr7yBpyXnSjgDju+Nx0xkDehhvY2W+08pr86HwPkyXV/pB7jwa8nyTO09G3b65m4+IiIhGjYs5c8TuuAvsmOODpGdhK+6bW2AM1rpvrhEmsDX3zTP8Eqx1f4iFZ+TyAKx1f8iF14Ctu2/ukWXK8T3YunB+WSrHPU9pnxKHcn5ZKseNr5TP8revLlw/aO9PzvMfYjxNu3+gdNZTKcFWuy/mwEzCFtruwlEL1ggftMZ2hi1LX1aswVaK7nATsEXhfMoGtuhezMH4hHwdo6byPk/JeewZpR9vzpdnaQpbE8YBz4O1Y+64X1Dq7yjnp9QPLQ+V66OOMyZfnqrxzPn+pB2h33c7Tq3mfq+bO8j9OGzCFlrueHFHOU9g5fkgexK2JOSJJ7QdAExB6cdGPr4J9flK6ydSHqQ55xOtnwr9ZItc/bCXPJin840W73c+IiIiGjX+zIqIiIiIiIiIaAHhYg4RERERERER0QLCn1kREREREREtBhvXAeGG7rH1989tW4hoqLiYQ0REREREtNBtXAdccQTQEh5WXqwBtaVz1yYiGpqBLOY8+OCDWLJkCSYmpneSuf322/FP//RP2HPPPfG///f/RrlcHsRhiIiIiIiIqJtww/RCzuqrgMmV3V9TWwosWT637SKioRjIM3NOOeWUme0ef/jDH+LUU0/FihUr8NOf/hRvfvObB3EIIiIiIiIi0kyuBJat6v4fF3KIFo2BfDMnjmMsW7YMAPDZz34Wb3rTm3DeeechyzIcdNBBgzjEnEvTFMYM7vnQjZaBaXXffjzoNAHP/e2loBMCnrstQSRstwwgiJtyPBS+ijlTv7ft5fPGA618pJQPlHgjZ3mt/Up59fy14y/ceKMhX/tZr5fyKIkBdI8BQNBMgJJ7a+sgSgAI8U4EMQ8SeXvoIGpCWj8PWi0lz5U81vphlCjllfcxUvIs7ziSN0+iRKk/5zgz8jxyt6/vPIqbMMXu/SmI20Am9MNmG2KetWK5H7cD6W3S57NmB+L7HMWAJ81XOftxT/ONe2v1INCOn3c+G/J8tYjj/eYRERHRqA1kMSdN05nFj9tuuw3/8A//AADwhBuq+SgMQxhj4Ps+isXiNtWRJAmS5A8fmqampgAAttCBLTg+7JUtbEV4K9IKbFWIGwtr6+6414CtyT91s/WaO+gXYa0V4gU57hkl7uWPS+3PUjmedmBrVTlel66vcn5AvvPLsnzltePnjWeZfH2E8mmadv13Zx5Vi7BVR252ElhP+CBYKcAaYUHE82CN8EGqMAFb7N5eAIBfh60L/ahYk/M0jJR+FsLW3ItNgPY+TsFWS+5wVoO1Qp4YX6lf6YfGKHmq9KNOS75+piCXV/NIyWOtfSPM09SxduDMo1rN/V7Em2ALHXcjSj6s3xLiVdiKMH+2DGzRvdiBsXG5n4SJ0g98uTwg55H2Pql5AL19dkxpnzCOaP0M2nyj9XNlvtTqX8Bx13xEREQ0Xw3kqyennnoqXvSiF+FVr3oVjDE4/vjjAQC/+tWvMDYm37TMFz/5yU/w4he/GMceeyye/vSn44orrsAvfvGLvuu57LLLMDExMfPf8uX8KiNRv5hHRPkxj4iIiIgWr4Es5vzt3/4tLr74Yrz61a/Gd7/73ZmfJ3U6HXzsYx8bxCGG6oEHHsBxxx2HZz3rWTjnnHNw8skn4x/+4R/w7ne/G9/5znf6quv888/Hpk2bZv5bt27dkFpNtHgxj4jyYx4RERERLV4D+ZnVhRdeOPO/f/zjH8+KP/vZzx7EYYbmq1/9Kp75zGfi4x//OADgNa95DY466ih88pOfxKWXXor3ve99OOyww3qqq1wuc/cuopyYR0T5MY+IiIiIFq+BfDPnwgsvxM033wwAmJycxNKlS7f6b77zPA+/+c1v8Pjjj8/826mnnoqzzz4bU1NT+MxnPoNNmzaNsIVERERERERERNMG8s2chx9+GF/60pdwyy23oFQq4ZRTTsErXvEK7LDDDoOofuh23313bN68GT/5yU9w/PHHo9PpwPd9nHzyyfjtb3+L8847D29605vwrGc9a9RNJSIiIiIiIqLt3EC+mbPrrrvi7W9/O26//XZcc8012LRpE/bff3985jOfGUT1Q/eyl70Mz3ve83DGGWfgwQcfhO/7aLend55605vehF133RX/+q//OuJWEhEREREREREN6Js5AJBlGe68807ceOON+N73vofTTjsNz33ucwdV/dBs2VL94x//OFavXo0XvOAF+MpXvoL9998fABBFEXbccUfsuuuuuY7TaPkwLb9rLGg2AdM9Nh2PAM+97hY024CwDXwQRoCwS3wQJnL5SNjOGUAQhDnjgRwPc5ZX4znbHwnbYfd0/JzxoV+f4bWv0WiIZWe9vgm4dhcPmgBK7ueDBM2mHG+3Ac+9pXKQJnIetrQ8bSl5GkNKVLUfauW1fqL146H3c+385nkejjBP+86juAlT7J5IQdIBMqEfNVOgJORJFMvzWdJU8qQDsR9HynyVux8l8vHzvo9hlK/+MMp5/JzzXd7jz+N4v3lEREQ0agNZzHn729+Ou+66C8ceeyxe+9rX4hOf+MQgqh2aLMvg/f5mcMvOWzvttBP+8R//EW984xtx3HHH4f/8n/+DnXfeGffddx9+8Ytf4Nhjj811TFtKYUtp92B5CWy95i4cp7D1ijvezGCtdceND2vr7nghlst7U7C1qjsOD7Yu1N9py+cHKMc3cjzL5OPDk8trcePni0M5v7xxTzu/IR9fiwvtS1NHTriOU6vA1hy5UCjDloUvGzYi2FLmjieZHEcNtiIMmaWynKeFspyHfkm/zlIeeQbWSnGln/iBEi/m7CfKOKXmqVI+b/u065Mpx+8lD6VxKku3eZzsO4/qNXdfbI/BloTVf8/AFoU88eqw1ZI7XirL/TRM5OvgF3qYL4TrrI3XftjD+yjNh73Mx0I87eQrr/XDvPNF7vlYqT9v+RzxfvOIiIho1AaymPPxj38cO+64Ix588EFcd911MwslWxZNfve73w3iMLn86le/wj333INXvOIVM+37Y3vvvTduu+02vPOd78Qtt9yCDRs2YJdddsHtt9+Offfdd45bTEREREREREQ020AWc+b7XzPuvfdevOQlL8EJJ5yAQw89FHvuuSeArb+hs+XnVgBw+eWXY8OGDSgUCjDGYGxsbGRtJyIiIiIiIiJ6qoE8ANnld7/7HT7+8Y8P8xCqBx98ECeccAJOO+00XH311TMLOQC6LuRs2Z586dKlmJiY4EIOEREREREREc0rA3sA8hZPPPEEbrrpJtx444349a9/jZe97GWDPkRf7rrrLjznOc/B+9//frTbbVx00UVYt24dfN/HGWecgcMPPxzl8vRDUc877zw89NBD+MAHPoAVK1aMtN1ERERERERERN0MZDFn06ZNuOWWW/D5z38ev/zlL/Hyl78cP/vZz/Dwww8Povpcfv7zn2NqagqdTgcveMELkGUZ9t9/f3znO9/B3XffjTe96U143eteh0KhgOc+97m49tprUSy6d+ogIiIiIiIiIhqlgSzm7LzzzjjiiCNw+eWX46ijjgIA3HTTTYOoOrdly5YBAO68805Ya3Hddddhxx13BACsWbMGa9euxamnnoolS5Zg9erV+NM//VN1JwQiIiIiIqI5t3EdEG7oHlt//9y2hYhGaiCLOWvXrsUXvvAFnHHGGXj5y1+OU045xblj1Fx76Utfine84x1485vfjL333hv1p2x5+clPfhKTk5O47bbb8MpXvhIAtooPUqPlw7S6X+6gEwGe+/FFQdIGhMsZJG3x2EEYKvE4X/koynl8LZ63fqV8EDA+onij0RDLznp9ksKUuj9wPYgSICu72xG3APjueNICPPe38oJ2IudpKwKEcS+IlTyNEzmu5qGWxznzKNDieftJ3vpzllfPf9h5su3jWN95FCYwfvf+EkRNIHPfGgRRG8iEPOrEch60OnJc68dRDGlC7O06CuXz9oO8ebTY57OhX9+5m4+IRmLjOuCKI4CWkAvFGlBbOndtIqKRGchizhlnnIEzzjgDTz75JG6++Wa8613vwmOPPYbzzjsPL3/5y3HssccO4jB963Q62HXXXfGxj30M73jHO/Doo4/i8ccfxx577AEAiKIIq1atmvn2DoChLULZWgm2VnJEi7DFzF3YVGHrVSHehK266gbgGeXbRplcHlYu73mw9ZpQfaqXt8IimjGwVn4QtVi/8Xs4vhDPUtiacP214zPujPe7E54tprBFR5mOgfVb7sLFDNYIHxRrY7AV94dUZHXYesUdDxPYijSkGrl+lJU8HJevcyFS3gclTwFYaTE7y+Q8yDJ9nNHal6e8Os710E+1888zjmjHV8t7zvalnY543FntqBRgq46+2izBloW9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hg341Kc+1XXh4s///M9x7rnn4gUveMHQn0EzSv1ck6d6zWteg/PPPx9RFKFareJzn/scjjvuOCxbtmyr13mehzVr1uCDH/wgNm3ahImJCQDA448/jq9//et417veNavugw8+GNdff/0Azm5+WzRbk3c6HVx44YU47bTTsHbtWi7kEBEREdHcW7J8eqHH9R8XcogWrLe85S1b/f9jjz0WGzZswNTU1KzXbtq0CQ899BAOOuggtd7nP//5uO6669TX7bXXXvj2t7/de4PnQD/X5KlOPfVURFGEr3zlK9i8eTO+8pWv4DWveU3X1772ta9FkiT40pe+NPNvN954I9rtdteFsj9u02K1qL6Z84EPfAATExPOZwEQERERERERbYs999xzq/+/ww47AACefPJJjI+PbxW79957AaCnxZyFrJ9r8lQ77bQTXvjCF+L6669HGIbodDp45Stf2fW1z3jGM3D44Yfjc5/7HF7/+tcDmP6J1ZFHHomnPe1ps16/9957b+vpLCiLZjEH+EPHISIiIiIiIhok168/uj2XZctizsEHHzzUNo1aP9fkj73mNa/BG9/4Rjz22GM48cQTxWcKvfa1r8Vf/uVf4uGHH0aSJLjrrrvwsY99rOtrq1X5+XuLBb/CQkRERERERDRA9957L3bbbTdMTs5+2Pn3v/99HHTQQRgfH8db3vKWrR7C/tBDD+HP/uzPsHTpUuy///74t3/7N+cxLr30UqxYsQLj4+M46qijZhaQLr30Upx55plbvfa4446bd8+RecUrXgFjDO666y7nT6y2ePWrXw3f93HDDTfgc5/7HIrFIl71qlfNUUvnJy7mEBEREREREQ3QQw89hD322GPWvzebTaxevRpvfetbsWHDBhx44IH4zne+A2B6Z72TTjoJL37xi/Hb3/4W11xzDU4//XT89re/7XqMZzzjGfjBD36ADRs24EUvehFe+9rXAgD+4i/+Av/0T/+EOI4BAA8//DDuvvtunHzyyUM6221jrcUnPvEJvO9978NJJ50kvnZychInnngirrvuOnzuc5/DCSec0HWhbHuyqH5mNR9t+XrZbx/fgDBudn1N3GyhEYbOOuIkQSMQ4s0WGo3AHY9jNBqNfPEgZ/15yzO+KOObN28GoH8Ns6c8SlpyniRNOc8ipZ8220oeNuXyUajUr5VX2pc0R/w+R3qei/GE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PPTQQ7j88svxmc98BgDQbrdx9913Y9myZahUhL8yEBERERERERHNge1qMSdN01lfo03TFJ7nIU1TPOtZz8KNN96ISqWCSy+9FCtWrMCLX/xiXHvttfjQhz6EmvjVayIiIiIiIiKi4dtunplz33334e/+7u/w8MMP42lPexpe8IIX4NWvfjWMMeh0OvB9H2ma4oADDsDatWvxwAMP4Ktf/Sr22GMPXHnlldhvv/1GfQpERERERERERNvHYs7PfvYzHHPMMVi9ejVe+tKX4utf/zre85734D//8z/x0Y9+FL7vo9lsolSaftrZ5OQkJicncdhhh4245UREREREREREW1v0P7NKkgQXX3wxTj/9dFx99dU499xz8eUvfxljY2O44oor8JrXvAYAZhZyPv3pT2PdunWjbDIRERERERERkdOi/2ZOuVzGY489NvMzqTiOUalU8KIXvQj77LMPfv7zn+MDH/gA3vGOd+Db3/42LrvsMnzjG9/AtddeC98XtvPuU6O8G4zjAcpB3AJ89/N4gnYCeO51t6ATyvEoATz3VrBB0pLjgbD9IoAgELZPBBCEkVx/GAOQ4kr9QQDAse37TP1aeca3Pd7D++/QaAhbHHd7fVaHybrnSpB2gOJO7nYkLaC8mzveNkBxR3c8bgHGvVXrdB659zIN0h7yVMqDVlPOo0jLo0iOq++jEo+E7ax7qT8StlnFoPqxME7kPv8hjzPC8fvOoyZgHJc7aPuAv8Tdjlas55kv5EnUBIx7bg1asdzP2wH0fu6m5oH2PkZK+wKlfq0fqXFtPh7yeJ+3fT3N50OMDzKPGg0Y031Mzz1vDiQu3RdFPcSF+sNe6hfKRznrT9qQPsJMz5c55tuoCWRS/R0ARSHelu8Hmk3I7feAknvr+CDpABX31upBJwH8CXc87QDFXdzxtgEKS4X2hUB1j66xRiqPoUQ0WIt6MSfLMkRRhGazif/5n/9Bu91GpVLBb37zG9x4441473vfi2984xv46le/ine84x045phj8M53vhN/8id/MtCFHACwxQy26JiYrIUtuScVeGVY95wAeDXYmntQh1+Cte5JAUEIW3PffANGLg/Icc/AWivEfTkOwNaF+tM2bE3YaczT2p8p7ff09knxLNWvn3R+WSbHteMPO65dH+H9/+MHkmts83ewTUdfrS6FzTa7C5d3hE2n3HG/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we7duxEdHe3r5tZb3333HcaMGYPo6GjExMSgrKwMW7ZswZ49e/jKbA2WLFmCxYsX4+jRo4iKivJ1cwJWVlYW3nzzTWzYsAHfffcd4uLifN0kl3j7ANUYB3KIAhe/mHoHv6DWT8wXz2KeuGaz2WAwGJCfn4+1a9c6ltvtdnTv3h1Dhw7FJ598AqvVCv4OWzvX9vG2bdswceJEfPzxx0hLS8OIESMwePBgrF+/Hps2beJAjpf169cPO3bswKBBg3Dy5EnIssyBHA0qjwl//OMf0bFjR5SWlvq4RYHLarXi+PHjKCoqwu7du/16IAfgnTlERERERD5TebdIfn4+OnfujNGjR1cZ0AGAL774At26dcONN97oo1YGtmv7uFOnThgzZoyjj4uLi5GTkwO9Xo927drxjoY6ZrfbAXCKAk8QQsBsNvOHd42sVisqKioCoh85mENERERE5APXDjLceuutSE5OxltvveV43IqP+mmn1Md8YQURBTIO5hARERER1bHfDjL8/ve/xzvvvON03hyqHfYxEdVnHMwhIiIiIqpDNpsNer2egwxexD4movqO9xUSEREREdUhvV6PkydPolu3bhg2bBjeffddDjJ4GPuYiOo73plDRERERFSHbDYb/vCHP0CSJLz55pscZPAC9jER1XcczCEiIiIiqmOXLl1CkyZNOAGvF7GPiag+42AOEREREREREVEA4TA1EREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAONRj9+/eHJEmQJAnp6eleq2fChAmOejZv3uy1eoh8gXlEVHeYb1Rf9O/fH1OnTvV1M4g8rj7t267OOf7+GRvyOYyDOdSgTJo0CQUFBejevXuV5XfccQckScJLL71UZbkQAr169YIkSViwYIFbdaxY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5JNPeuwxMCJv27t3LzZu3IjNmzc7Xu+qlh/MIwo0MTExOH/+PHbv3l1tMlNnd6h069YN3333HT7++GN07twZ3bt3xzPPPIPbb78d8fHxWLJkSa3b0r17d7z88svo0qULdDodxo4dq7g+843IuaVLl8JsNuOBBx6o8oryl156qdqv+Q888AA++eSTKss6duyIxx57rMoyo9GIRx55BN26dUP37t1ht9vxpz/9ydsfhQgAkJiYiHvvvRcxMTEYO3as4+UV3bp1w+zZs3HXXXchNjYWQ4YMqfayisGDB6OkpARdu3bFokWLqsydFh4ejp49e1Z7FbgzWs85Wl26dAmfffYZdu3ahaefftqtMg35HCYJIYSvG0FUF3755RdYLBYAQLt27RyT5Hna+fPnUVxcDODqa/o44zrVJ8wjotrLz8/H8OHD3Z5UlflGRERaVVRUID4+Hjt27MD111/vcr26OucoueGGG3Dp0iXMnTvX8eZINQ35HMbBHCIiIqI6UNPBHCIiIi3S09ORnJyMxx57DPPmzfN1c8jDOJhDRERERERERBRAOGcOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA4WAOEREREREREVEA+X9jrQxP8uIYJgAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import numpy as np\n", - "from compas_python_utils.cosmic_integration.binned_cosmic_integrator.bbh_population import generate_mock_bbh_population_file\n", - "from compas_python_utils.cosmic_integration.binned_cosmic_integrator.bbh_population import BBHPopulation\n", - "\n", - "np.random.seed(42)\n", - "\n", - "m1_min = 5\n", - "m1_max = 150\n", - "m2_min = 0.1\n", - "\n", - "compas_fname = generate_mock_bbh_population_file(\n", - " \"mock_compas_data.h5\", n_systems=int(1e4), frac_bbh=1,\n", - " m1_min=m1_min, m1_max=m1_max, m2_min=m2_min\n", - ")\n", - "bbh_population = BBHPopulation.from_compas_h5(compas_fname, m1_min=m1_min, m1_max=m1_max, m2_min=m2_min)\n", - "fig = bbh_population.plot()" - ] - }, - { - "cell_type": "markdown", - "id": "db853456", - "metadata": {}, - "source": [ - "In order to calculate the formation rate of BBHs at a given redshift, we need to know:\n", - "\n", - "- The grid of metallicities we assume for the integral\n", - "- The amount of solar mass evolved per metallicity per system $dZ/dM_{\\odot}$\n", - "- Each DCOs'\n", - " - metallicity at formation\n", - " - delay time $t_{\\rm delay}$ (time from formation till merger)\n", - " - merger time $t_c$ for each DCO\n", - " - component masses $m_1, m_2$ at $t_c$ (for selection effects)\n", - "\n", - "Given a time at which the BBH merges we can then calculate the time at which the BBH formed to recover the MSSFR ($dM_{\\odot}/dt)$" - ] - }, - { - "cell_type": "markdown", - "id": "30b3671b", - "metadata": {}, - "source": [ - "## Select a MSSFR model\n", - "\n", - "\n", - "The universe evolved over time and as such the star formation rate and the metallicity of the starforming gas change. The metallicity-specific star formation rate (MSSFR) determines the amount of star formation that went into forming a system born at redshift z, in a metallicity bin dZ.\n", - "\n", - "A schematic picture of how the MSSFR is constructed is given in Figure 10 of the COMPAS methods paper and added below (note that SFRD stands for Star Formation Rate Distribution, which is the same as the MSSFR).\n", - "\n", - "![https://raw.githubusercontent.com/TeamCOMPAS/COMPAS/dev/misc/examples/Tutorials/SFRD_cartoon.png](https://raw.githubusercontent.com/TeamCOMPAS/COMPAS/dev/misc/examples/Tutorials/SFRD_cartoon.png)\n", - "\n", - "In the Cosmic-integrator code, this can be done by using the `CosmologicalModel` class (the parameters are taken from [Neijssel et al. 2020](https://arxiv.org/abs/1906.08136)):\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "6a77264f", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "data": { - "text/html": [ - "
✔️ 3.53 s (2023-09-11T17:49:51/2023-09-11T17:49:55)
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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+PBhrFYrKpX46ysIQuMnftNV0z8puQDEuuCOetGiRQC88MILziQN0Lx5cx599FGef/75MuevW7eOl156qdrtx8XFlUvUDz30EGazmXnz5tU+8AoUFhbyww8/4OHhUeEXgLw8xxecijZvKd1VpvScS51f+hq73U5BQUG5/a8FQRAaI9H1XQ25RWaSsosB6Bxe9zvq/fv3AzB48OByz1V07MUXX0SW5Wo//n33vXjxYn744Qc++OADQkND6xz/v9suLCzklltucc0G6YIgCEIZIlFXw7E0R9nQFv56fD3UdW4vLy8PhUJBUFBQuedCQkLq3P7FsrOzmTp1Ktdeey0TJ050adtQdbc3XLgzvviuuVR+fn6Zcy51fulrJEnC29u79kELgiA0IKLruxqOnnMklE7hrrlj9PX1xW63k5mZSXBwcJnnzp07V+78uoxRnz17lqysLJYtW1ZpNbWwsDAA9u7dW+EYeWUOHz7M1q1bad++PYMGDarwnJiYGHbt2kVCQgKBgYFlnqtoPLqicetSNpuNxMREWrZsKcanBUFoMsRvu2o4UnJH3ckF3d4AsbGx7Nmzh40bN3LjjTeWea6iql51GaMODAwst2Sq1LJly0hPT+c///kPer2+XCK9lMqWZP07lu+++44VK1bQr1+/Ms8tX77cec7F5wOsWLGCp59+usz5mzZtwmAwlDlfEASh0XPJauxGqnTRevzry+Sop/6QVx1Od0m7a9askQE5NjZWLiwsdB5PTk6Wg4KCLmvBk4tVVfAkNTVVPnLkiJybm1vha81msxwcHCyr1Wr53LlzlV4jOztb9vX1dWnBk82bN9f0rQqCIFwxouCJGyRmFgGuu6OOj49n8uTJ7N+/ny5duvDYY4/x8MMP061bt3J3ne4yc+ZMOnToUKYYycV+++03MjIyGDNmTJVrsv39/ZkzZw6ZmZn06NGDRx55hEceeYQePXqQlZXF//73v3Ljzf/73//w9fVl7Nix3HnnnTz11FP06NGDjRs38vDDDzNgwACXvldBEIT6THR9V4PNLhPkqSHEp+4VyUp9+umntG3blk8//ZQ5c+bQokULZsyYwfjx4/njjz9cdp3L5VKTyC52xx13EBQUxOuvv87ChQuRJImePXvy3HPPMXz48HLnd+rUie3bt/Pcc8+xbNkyDAYDbdu2Ze7cuTz44IMufy+CIAj1mSTLYjuiyuTn5+Pr60vEtB+I6xzJV/f0dXdIgiAIQj1Xmjvy8vJcsmxVdH1XU0cXzfgWBEEQhJoQibqaXDU+LQiCIAg10agT9bFjx+jWrZvzodfrWbp0aa3actUaakEQBEGoiUY9maxdu3bs27cPcNSkjo6OZsSIETVuR6dWEB3oeekTBUEQBMHFGvUd9cV+++03hg0bVmYTjOpqF+KNUlFxVS9BEARBuJzqdaLesGEDY8aMITw8HEmSKuy2njt3LtHR0eh0Ovr27cuOHTsqbOuHH37g1ltvrVUcHcJEt7cgCILgHvU6URsMBmJjY5k7d26Fzy9evJgZM2Ywa9Ys9uzZQ2xsLKNGjeL8+fNlzsvPz2fLli1cc801VV7PZDKRn59f5gHQPlRsACEIgiC4R71O1KNHj+bVV19l3LhxFT7/7rvvMmXKFCZPnkzHjh35+OOP8fDwYMGCBWXO+/XXXxk5ciQ6na7K682ePRtfX1/nIyIiAhB31IIgCIL71OtEXRWz2czu3bvLVLZSKBQMHz6crVu3ljm3ut3eM2fOJC8vz/lISkoCoHUzL9cGLwiCIAjV1GBnfWdmZmKz2crt3xwSEsLRo0edP+fl5bFjxw5+/vnnS7ap1WrRasuXCdWplXUPWBAEQRBqocEm6ury9fWtcI9nQRAEQWgIGmzXd1BQEEqlslwSPnfuHKGhoW6KShAEQRBcq8Emao1GQ8+ePVm9erXzmN1uZ/Xq1fTv39+NkQmCIAiC69Trru/CwkJOnDjh/DkxMZF9+/YREBBAZGQkM2bMYNKkSfTq1Ys+ffrw/vvvYzAYmDx5shujFgRBEATXqdeJeteuXcTHxzt/njFjBgCTJk3iiy++4NZbbyUjI4MXXniB9PR0unXrxt9//11ugpkgCIIgNFRiP+oquHpPUUEQBKHxc3XuqNd31O4yd+5c5s6di9VqBXBWKBMEQRCESynNGa66DxZ31FU4deoUrVu3dncYgiAIQgN08uRJWrVqVed2xB11FQICAgA4e/Ysvr6+bo6m4cnPzyciIoKkpCQxdFAL4vOrG/H51Z747OomLy+PyMhIZw6pK5Goq6BQOFav+fr6ir+sdeDj4yM+vzoQn1/diM+v9sRnVzelOaTO7bikFUEQBEEQLguRqAVBEAShHhOJugparZZZs2ZVuFGHcGni86sb8fnVjfj8ak98dnXj6s9PzPoWBEEQhHpM3FELgiAIQj0mErUgCIIg1GMiUQuCIAhCPSYStSAIgiDUYyJRV2Hu3LlER0ej0+no27cvO3bscHdIDcLs2bPp3bs33t7eNGvWjLFjx3Ls2DF3h9UgvfHGG0iSxLRp09wdSoORkpLCHXfcQWBgIHq9ni5durBr1y53h9Ug2Gw2nn/+eVq2bIler6d169a88sorLqtZ3dhs2LCBMWPGEB4ejiRJLF26tMzzsizzwgsvEBYWhl6vZ/jw4SQkJNT4OiJRV2Lx4sXMmDGDWbNmsWfPHmJjYxk1ahTnz593d2j13vr165k6dSrbtm1j5cqVWCwWRo4cicFgcHdoDcrOnTuZP38+Xbt2dXcoDUZOTg4DBw5ErVbz119/cfjwYd555x38/f3dHVqD8OabbzJv3jzmzJnDkSNHePPNN3nrrbf46KOP3B1avWQwGIiNjWXu3LkVPv/WW2/x4Ycf8vHHH7N9+3Y8PT0ZNWoURqOxZheShQr16dNHnjp1qvNnm80mh4eHy7Nnz3ZjVA3T+fPnZUBev369u0NpMAoKCuSYmBh55cqVclxcnPzoo4+6O6QG4amnnpIHDRrk7jAarGuvvVa+++67yxy78cYb5dtvv91NETUcgPzLL784f7bb7XJoaKj89ttvO4/l5ubKWq1W/u6772rUtrijroDZbGb37t0MHz7ceUyhUDB8+HC2bt3qxsgapry8PACXFahvCqZOncq1115b5u+gcGm//fYbvXr14pZbbqFZs2Z0796dTz/91N1hNRgDBgxg9erVHD9+HID9+/ezadMmRo8e7ebIGp7ExETS09PL/Dfs6+tL3759a5xHxKYcFcjMzMRmsxESElLmeEhICEePHnVTVA2T3W5n2rRpDBw4kM6dO7s7nAbh+++/Z8+ePezcudPdoTQ4p06dYt68ecyYMYNnnnmGnTt38n//939oNBomTZrk7vDqvaeffpr8/Hzat2+PUqnEZrPx2muvcfvtt7s7tAYnPT0doMI8UvpcdYlELVxWU6dO5eDBg2zatMndoTQISUlJPProo6xcuRKdTufucBocu91Or169eP311wHo3r07Bw8e5OOPPxaJuhp++OEHvvnmG7799ls6derEvn37mDZtGuHh4eLzcyPR9V2BoKAglEol586dK3P83LlzhIaGuimqhufhhx/mjz/+YO3atbRo0cLd4TQIu3fv5vz58/To0QOVSoVKpWL9+vV8+OGHqFQqbDabu0Os18LCwujYsWOZYx06dODs2bNuiqhheeKJJ3j66ae57bbb6NKlCxMnTmT69OnMnj3b3aE1OKW5whV5RCTqCmg0Gnr27Mnq1audx+x2O6tXr6Z///5ujKxhkGWZhx9+mF9++YU1a9bQsmVLd4fUYAwbNowDBw6wb98+56NXr17cfvvt7Nu3D6VS6e4Q67WBAweWWwp4/PhxoqKi3BRRw1JUVFRuD2WlUondbndTRA1Xy5YtCQ0NLZNH8vPz2b59e43ziOj6rsSMGTOYNGkSvXr1ok+fPrz//vsYDAYmT57s7tDqvalTp/Ltt9/y66+/4u3t7RyP8fX1Ra/Xuzm6+s3b27vcWL6npyeBgYFijL8apk+fzoABA3j99dcZP348O3bs4JNPPuGTTz5xd2gNwpgxY3jttdeIjIykU6dO7N27l3fffZe7777b3aHVS4WFhZw4ccL5c2JiIvv27SMgIIDIyEimTZvGq6++SkxMDC1btuT5558nPDycsWPH1uxCLpqZ3ih99NFHcmRkpKzRaOQ+ffrI27Ztc3dIDQJQ4WPhwoXuDq1BEsuzaub333+XO3fuLGu1Wrl9+/byJ5984u6QGoz8/Hz50UcflSMjI2WdTie3atVKfvbZZ2WTyeTu0OqltWvXVvi7btKkSbIsO5ZoPf/883JISIis1WrlYcOGyceOHavxdcQ2l4IgCIJQj4kxakEQBEGox0SiFgRBEIR6TCRqQRAEQajHRKIWBEEQhHpMJGpBEARBqMdEohYEQRCEekwkakEQBEGox0SiFgRBEIR6TCRqQRAuad26dUiSRG5urkvOffHFF+nWrVu5YyEhIUiSxNKlS+sUryA0JiJRC0Ijc9dddyFJEpIkoVaradmyJU8++SRGo9HdoTk9/vjjZTYrOHLkCC+99BLz588nLS2N0aNHEx0dzfvvv+++IAWhnhCbcghCI3T11VezcOFCLBYLu3fvZtKkSUiSxJtvvunu0ADw8vLCy8vL+fPJkycBuOGGG5AkyV1hCUK9JO6oBaER0mq1hIaGEhERwdixYxk+fDgrV64EHFu2zp49m5YtW6LX64mNjeWnn34q8/o///yTtm3botfriY+P5/Tp02WeP3PmDGPGjMHf3x9PT086derEn3/+Weac3bt306tXLzw8PBgwYECZ7Scv7vp+8cUXGTNmDAAKhQJJkhgyZAhnzpxh+vTpzt4BQWiqRKIWhEbu4MGDbNmyBY1GA8Ds2bNZtGgRH3/8MYcOHWL69OnccccdrF+/HoCkpCRuvPFGxowZw759+7j33nt5+umny7Q5depUTCYTGzZs4MCBA7z55ptl7pABnn32Wd555x127dqFSqWqdKvExx9/nIULFwKQlpZGWloaS5YsoUWLFrz88svOY4LQVImub0FohP744w+8vLywWq2YTCYUCgVz5szBZDLx+uuvs2rVKufm9a1atWLTpk3Mnz+fuLg45s2bR+vWrXnnnXcAaNeunTMZlzp79iw33XQTXbp0cbbxb6+99hpxcXEAPP3001x77bUYjUZ0Ol2Z87y8vPDz8wMgNDTUeVypVOLt7V3mmCA0RSJRC0IjFB8fz7x58zAYDLz33nuoVCpuuukmDh06RFFRESNGjChzvtlspnv37oBjYlffvn3LPF+a1Ev93//9Hw8++CArVqxg+PDh3HTTTXTt2rXMORf/HBYWBsD58+eJjIx02fsUhKZAJGpBaIQ8PT1p06YNAAsWLCA2NpbPP/+czp07A7Bs2TKaN29e5jVarbba7d97772MGjWKZcuWsWLFCmbPns0777zDI4884jxHrVY7/710jNlut9f6PQlCUyXGqAWhkVMoFDzzzDM899xzdOzYEa1Wy9mzZ2nTpk2ZR0REBAAdOnRgx44dZdrYtm1buXYjIiJ44IEHWLJkCY899hiffvqpS+PWaDTYbDaXtikIDZFI1ILQBNxyyy0olUrmz5/P448/zvTp0/nyyy85efIke/bs4aOPPuLLL78E4IEHHiAhIYEnnniCY8eO8e233/LFF1+UaW/atGksX76cxMRE9uzZw9q1a+nQoYNLY46OjmbDhg2kpKSQmZnp0rYFoSERXd+C0ASoVCoefvhh3nrrLRITEwkODmb27NmcOnUKPz8/evTowTPPPANAZGQkP//8M9OnT+ejjz6iT58+vP7662VmbdtsNqZOnUpycjI+Pj5cffXVvPfeey6N+eWXX+b++++ndevWmEwmZFl2afuC0FBIsvjbLwiCIAj1luj6FgRBEIR6TCRqQRAEQajHRKIWBEEQhHpMJGpBEARBqMdEohYEQRCEekwkakEQBEGox0SiFgRBEIR6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MiI2btyICy+8EM8999ywTPqpdbquY9q0abjmmmsAGNG869atw1133bVDFOo//elP+MMf/oCHHnoI+++/P9auXYuLLroIEyZM2CF+fqpNXPquoqGhAYqioL29vez+9vZ2jBs3bpSOauT9+Mc/xhNPPIEXX3wRX/nKV0b7cEbE6tWrsXnzZhx88MGw2Wyw2Wx46aWXcOutt8Jms0HTtNE+xGE1fvx47LfffmX37bvvvmhtbR2lIxpZP//5z7Fo0SJ8//vfxwEHHIDTTjsNCxYswLJly0b70EZc8Xfdjv57sBawUFfhcDgwdepUrFixwrpP13WsWLECM2fOHMUjGxlCCPz4xz/GY489hhdeeAG77bbbaB/SiDnyyCPx7rvvYu3atdZt2rRpOPXUU7F27VooijLahzisDj300IpWvI8++gi77LLLKB3RyEqn0xUzmBVF6XGW8fZst912w7hx48p+D8bjcbzxxhs7xO/BWsKl7x4sXLgQ8+bNw7Rp0zB9+nTccsstSKVSOPPMM0f70Ibd/Pnz8dBDD+Fvf/sb/H6/tR8VDAbhdrtH+eiGl9/vr9iL93q9qK+v3yH26BcsWIBZs2bhmmuuwfe+9z28+eabuOeee3DPPfeM9qGNiLlz5+Lqq6/GzjvvjP333x/vvPMObr75Zvzwhz8c7UMbFslkEp988on19WeffYa1a9eirq4OO++8My666CL86le/wp577onddtsNl19+OSZMmIATTjhh9A56RzTal53Xsttuu03svPPOwuFwiOnTp4vXX399tA9pRACoerv//vtH+9BGxY7UniWEEP/4xz/EpEmThNPpFPvss4+45557RvuQRkw8HhcXXnih2HnnnYXL5RITJ04Ul156qcjlcqN9aMPixRdfrPp3fd68eUIIo0Xr8ssvF83NzcLpdIojjzxSfPjhh6N70DsgjrkkIiKqYdyjJiIiqmEs1ERERDWMhZqIiKiGsVATERHVMBZqIiKiGsZCTUREVMNYqImIiGoYCzVRDVi5ciUkSUI0Gh2Sx15xxRU46KCDKu5rbm6GJEl4/PHHt+l4iWjksFATDdAZZ5wBSZIgSRLsdjt22203/OIXv0A2mx3tQ7P87Gc/K8toXr9+Pa688krcfffd2LRpE4455hjsuuuuuOWWW0bvIImoX5j1TTQI3/zmN3H//fejUChg9erVmDdvHiRJwnXXXTfahwYA8Pl88Pl81teffvopAOD444+HJEmjdVhENAg8oyYaBKfTiXHjxqGlpQUnnHACZs+ejeeeew6AMWlt2bJl2G233eB2uzF58mT85S9/KXv+8uXLsddee8HtduOII47A559/Xvb9L774AnPnzkU4HIbX68X++++P5cuXlz1m9erVmDZtGjweD2bNmlU29ap06fuKK67A3LlzAQCyLEOSJHz961/HF198gQULFlirA0RUm1ioibbRunXr8Nprr8HhcAAAli1bhv/93//FXXfdhffeew8LFizAD37wA7z00ksAgI0bN+I///M/MXfuXKxduxZnn302Fi1aVPaa8+fPRy6Xw8svv4x3330X1113XdkZMgBceumluOmmm/D222/DZrP1OOHpZz/7Ge6//34AwKZNm7Bp0yY8+uij+MpXvoJf/vKX1n1EVJu49E00CE888QR8Ph9UVUUul4Msy7j99tuRy+VwzTXX4Pnnn7dm9k6cOBGvvPIK7r77bhx++OG48847sfvuu+Omm24CAOy9995WMS5qbW3Fd7/7XRxwwAHWa2zt6quvxuGHHw4AWLRoEY477jhks1m4XK6yx/l8PoRCIQDAuHHjrPsVRYHf7y+7j4hqDws10SAcccQRuPPOO5FKpfDf//3fsNls+O53v4v33nsP6XQaRx11VNnj8/k8pkyZAsC4sGvGjBll3y8W9aKf/OQnOP/88/Hss89i9uzZ+O53v4sDDzyw7DGlX48fPx4AsHnzZuy8885D9nMS0ehjoSYaBK/Xiz322AMA8Nvf/haTJ0/Gfffdh0mTJgEAnnzySey0005lz3E6nf1+/bPPPhtz5szBk08+iWeffRbLli3DTTfdhAsuuMB6jN1ut/53cY9Z1/VB/0xEVJu4R020jWRZxiWXXILLLrsM++23H5xOJ1pbW7HHHnuU3VpaWgAA++67L958882y13j99dcrXrelpQXnnXceHn30Ufz0pz/FvffeO6TH7XA4oGnakL4mEQ09FmqiIXDiiSdCURTcfffd+NnPfoYFCxbgwQcfxKeffoo1a9bgtttuw4MPPggAOO+88/Dxxx/j5z//OT788EM89NBDeOCBB8pe76KLLsIzzzyDzz77DGvWrMGLL76Ifffdd0iPedddd8XLL7+ML7/8Ep2dnUP62kQ0dLj0TTQEbDYbfvzjH+P666/HZ599hsbGRixbtgwbNmxAKBTCwQcfjEsuuQQAsPPOO+Ovf/0rFixYgNtuuw3Tp0/HNddcU3bVtqZpmD9/Pv79738jEAjgm9/8Jv77v/97SI/5l7/8JX70ox9h9913Ry6XgxBiSF+fiIaGJPi3k4iIqGZx6ZuIiKiGsVATERHVMBZqIiKiGsZCTUREVMNYqImIiGoYCzUREVENY6EmIiKqYSzURERENYyFmoiIqIaxUBMREdUwFmoiIqIaxkJNRERUw1ioiYiIahgLNRERUQ1joSYiIqphLNREREQ1jIWaiIiohrFQExER1bAxU6hffvllzJ07FxMmTIAkSXj88cf7fM7KlStx8MEHw+l0Yo899sADDzww7MdJREQ0lMZMoU6lUpg8eTLuuOOOfj3+s88+w3HHHYcjjjgCa9euxUUXXYSzzz4bzzzzzDAfKRER0dCRhBBitA9ioCRJwmOPPYYTTjihx8dcfPHFePLJJ7Fu3Trrvu9///uIRqN4+umnR+AoiYiItt2YOaMeqFWrVmH27Nll982ZMwerVq0apSMiIiIaONtoH8BwaWtrQ3Nzc9l9zc3NiMfjyGQycLvdFc/J5XLI5XLW17quo6urC/X19ZAkadiPmYiIxj4hBBKJBCZMmABZ3vbz4e22UA/GsmXLcOWVV472YRAR0XZg48aN+MpXvrLNr7PdFupx48ahvb297L729nYEAoGqZ9MAsHjxYixcuND6OhaLYeedd8bGjRsRCASG9XiJiGj7EI/H0dLSAr/fPySvt90W6pkzZ2L58uVl9z333HOYOXNmj89xOp1wOp0V9wcCARZqIiIakKHaMh0zF5Mlk0msXbsWa9euBWC0X61duxatra0AjLPh008/3Xr8eeedhw0bNuAXv/gFPvjgA/z617/Gn/70JyxYsGA0Dp+IiGhQxkyhfvvttzFlyhRMmTIFALBw4UJMmTIFS5YsAQBs2rTJKtoAsNtuu+HJJ5/Ec889h8mTJ+Omm27Cb37zG8yZM2dUjp+IiGgwxmQf9UiJx+MIBoOIxWJc+iYion4Z6toxZs6oiYiIdkQs1ERERDWMhZqIiKiGsVATERHVMBZqIiKiGsZCTUREVMNYqImIiGoYCzUREVENY6EmIiKqYSzURERENYyFmoiIqIaxUBMREdUwFmoiIqIaxkJNRERUw1ioiYiIahgLNRERUQ1joSYiIqphLNREREQ1jIWaiIiohrFQExER1TAWaiIiohrGQk1ERFTDWKiJiIhqGAs1ERFRDWOhJiIiqmEs1ERERDWMhZqIiKiGsVATERHVMBZqIiKiGjamCvUdd9yBXXfdFS6XCzNmzMCbb77Z6+NvueUW7L333nC73WhpacGCBQuQzWZH6GiJiIi23Zgp1I888ggWLlyIpUuXYs2aNZg8eTLmzJmDzZs3V338Qw89hEWLFmHp0qVYv3497rvvPjzyyCO45JJLRvjIiYiIBm/MFOqbb74Z55xzDs4880zst99+uOuuu+DxePDb3/626uNfe+01HHrooTjllFOw66674uijj8bJJ5/c51k4ERFRLRkThTqfz2P16tWYPXu2dZ8sy5g9ezZWrVpV9TmzZs3C6tWrrcK8YcMGLF++HMcee2yP75PL5RCPx8tuREREo8k22gfQH52dndA0Dc3NzWX3Nzc344MPPqj6nFNOOQWdnZ047LDDIISAqqo477zzel36XrZsGa688sohPXYiIqJtMSbOqAdj5cqVuOaaa/DrX/8aa9aswaOPPoonn3wSV111VY/PWbx4MWKxmHXbuHHjCB4xERFRpTFxRt3Q0ABFUdDe3l52f3t7O8aNG1f1OZdffjlOO+00nH322QCAAw44AKlUCueeey4uvfRSyHLlv1GcTiecTufQ/wBERESDNCbOqB0OB6ZOnYoVK1ZY9+m6jhUrVmDmzJlVn5NOpyuKsaIoAAAhxPAdLBER0RAaE2fUALBw4ULMmzcP06ZNw/Tp03HLLbcglUrhzDPPBACcfvrp2GmnnbBs2TIAwNy5c3HzzTdjypQpmDFjBj755BNcfvnlmDt3rlWwiYiIat2YKdQnnXQSOjo6sGTJErS1teGggw7C008/bV1g1traWnYGfdlll0GSJFx22WX48ssv0djYiLlz5+Lqq68erR+BiIhowCTBdeAexeNxBINBxGIxBAKB0T4cIiIaA4a6doyJPWoiIqIdFQs1ERFRDWOhJiIiqmEs1ERERDWMhZqIiKiGsVATERHVMBZqIiKiGsZCTUREVMNYqImIiGoYCzUREVENY6EmIiKqYSzURERENYyFmoiIqIaxUBMREdUwFmoiIqIaxkJNRERUw1ioiYiIahgLNRERUQ1joSYiIqphLNREREQ1jIWaiIiohrFQExER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eaxevRqzZ8+27pNlGbNnz8aqVat6fN4vf/lLNDU14ayzzurX++RyOcTj8bIbERHRaBoThbqzsxOapqG5ubns/ubmZrS1tVV9ziuvvIL77rsP9957b7/fZ9myZQgGg9atpaVlm46biIhoW42JQj1QiUQCp512Gu699140NDT0+3mLFy9GLBazbhs3bhzGoyQiIurbiPRRb6uGhgYoioL29vay+9vb2zFu3LiKx3/66af4/PPPMXfuXOs+XdcBADabDR9++CF23333iuc5nU44nc4hPnoiIqLBGxNn1A6HA1OnTsWKFSus+3Rdx4oVK6xBH6X22WcfvPvuu1i7dq11+/a3v40jjjgCa9eu5ZI2ERGNGWPijBoAFi5ciHnz5mHatGmYPn06brnlFqRSKZx55pkAgNNPPx077bQTli1bZo3SLBUKhQCg4n4iIqJaNmYK9UknnYSOjg4sWbIEbW1tOOigg/D0009bF5i1trZClsfEAgEREVG/DfuYy7GMYy6JiGigxtyYSyIiIho8FmoiIqIaxkJNRERUw1ioiYiIahgLNRERUQ1joSYiIqphLNREREQ1jIWaiIiohrFQExER1TAWaiIiohrGQk1ERFTDWKiJiIhqGAs1ERFRDWOhJiIiqmEs1ERERDWMhZqIiKiGsVATERHVMBZqIiKiGsZCTUREVMNYqImIiGoYCzUREVENY6EmIiKqYbbRPoAdRU7VIMRoHwUREfXGaZMhSdJoH0YZFuoR8uiaL9EWy472YRARUS/mH7EHHLbaKtRc+iYiIqphLNREREQ1jIWaiIiohrFQExER1TAWaiIioho2pgr1HXfcgV133RUulwszZszAm2++2eNj7733Xnz1q19FOBxGOBzG7Nmze308ERFRLRozhfqRRx7BwoULsXTpUqxZswaTJ0/GnDlzsHnz5qqPX7lyJU4++WS8+OKLWLVqFVpaWnD00Ufjyy+/HOEjJyIiGjxJiLERwzFjxgwccsghuP322wEAuq6jpaUFF1xwARYtWtTn8zVNQzgcxu23347TTz+9X+8Zj8cRDAYRi8UQCAS26fj/+GYr+6iJiGqc0Ue9beewQ1k7gDFyRp3P57F69WrMnj3buk+WZcyePRurVq3q12uk02kUCgXU1dX1+JhcLod4PF52IyIiGk1jolB3dnZC0zQ0NzeX3d/c3Iy2trZ+vcbF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HfHOO+8IAOLmm28W77zzjvjiiy+EEEIsWrRInHbaadbji+1ZP//5z8X69evFHXfcwfassey2224TO++8s3A4HGL69Oni9ddft753+OGHi3nz5pU9/k9/+pPYa6+9hMPhEPvvv7948sknR/iIR95APqNddtlFAKi4LV26dOQPfIQM9M9QqR2hUAsx8M/otddeEzNmzBBOp1NMnDhRXH311UJV1RE+6pEzkM+nUCiIK664Quy+++7C5XKJlpYW8V//9V8iEomM/IGPkBdffLHq75Xi5zJv3jxx+OGHVzznoIMOEg6HQ0ycOFHcf//9I37cHHNJRERUw7hHTUREVMNYqImIiGoYCzUREVENY6EmIiKqYSzURERENYyFmoiIqIaxUBMREdUwFmoiwte//nVcdNFFo30YRFQFh3IQ0ZCTJKnX7zNniaj/WKiJaMht2rSp4r7PP/8cRx11FObNmzcKR0Q0dnHpm4jKRCIRnH766QiHw/B4PDjmmGPw8ccflz3m3nvvRUtLCzweD77zne/g5ptvRigUsr4/bty4slsgEMB5552HadOm4ZZbbhnZH4hojGOhJqIyZ5xxBt5++238/e9/x6pVqyCEwLHHHotCoQAAePXVV3HeeefhwgsvxNq1a3HUUUfh6quv7vU1zzzzTMRiMfz5z3+GzcaFPKKB4N8YIrJ8/PHH+Pvf/45XX30Vs2bNAgD84Q9/QEtLCx5//HGceOKJuO2223DMMcfgZz/7GQBgr732wmuvvYYnnnii6msuW7YMTz75JF599VU0NDSM2M9CtL3gGTURWdavXw+bzYYZM2ZY99XX12PvvffG+vXrAQAffvghpk+fXva8rb8uWr58OS6//HLcf//9mDx58vAdONF2jIWaiIbFRx99hFNOOQWLFi3CiSeeONqHQzRmsVATkWXfffeFqqp44403rPu2bNmCDz/8EPvttx8AYO+998Zbb71V9rytv47H4zj++OPxta99DVddddXwHzjRdox71ERk2XPPPXH88cfjnHPOwd133w2/349FixZhp512wvHHHw8AuOCCC/C1r30NN998M+bOnYsXXngBTz31lNU7LYTAqaeeinQ6jZtuugnt7e0V79PY2AhFUUb0ZyMaq3hGTURl7r//fkydOhXf+ta3MHPmTAghsHz5ctjtdgDAoYceirvuugs333wzJk+ejKeffhoLFiyAy+UCALS2tuKJJ55Aa2sr9tprL4wfP77itnHjxtH8EYnGFEkwIoiIttE555yDDz74AP/85z9H+1CItjtc+iaiAbvxxhtx1FFHwev14qmnnsKDDz6IX//616N9WETbJZ5RE9GAfe9738PKlSuRSCQwceJEXHDBBTjvvPNG+7CItkss1ERERDWMF5MRERHVMBZqIiKiGsZCTUREVMNYqImIiGoYCzUREVENY6EmIiKqYSzURERENYyFmoiIqIaxUBMREdWw/x8hOgeKOy+dbQAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from compas_python_utils.cosmic_integration.binned_cosmic_integrator.cosmological_model import CosmologicalModel\n", - "\n", - "cosmological_model = CosmologicalModel(\n", - " aSF=0.01, bSF=2.77, cSF=2.90, dSF=4.70,\n", - " mu_0=0.035, sigma_0=0.39, mu_z=-.23, sigma_z=0, alpha=0,\n", - " min_observed_log_metallicity=-4,\n", - " max_observed_log_metallicity=0,\n", - ")\n", - "\n", - "fig = cosmological_model.plot()" - ] - }, - { - "cell_type": "markdown", - "id": "b9d36dde", - "metadata": {}, - "source": [ - "## Accounting for selection effects and observation sensitivity\n", - "\n", - "Before we can calculate the formation rate of BBHs at a given redshift, we need to account for selection effects and observation sensitivity.\n", - "We determine if we can confidently select a system by choosing a signal-to-noise ratio (SNR), for which we often use 8.\n", - "The SNR of a binary depends on:\n", - "- its component masses,\n", - "- its distance and the sky-orientation (compared to the gravitational wave detector)\n", - "- the sensitivity of the detector\n", - "\n", - "After accounting for the above, we can compute a grid of SNRs for a grid of component masses (note: we can alter the distance and sky-orientation later on)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "49eed913", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
✔️ 694 ms (2023-09-11T17:49:55/2023-09-11T17:49:55)
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from compas_python_utils.cosmic_integration.binned_cosmic_integrator.snr_grid import SNRGrid\n", - "\n", - "snr_grid = SNRGrid(sensitivity='O1')\n", - "fig = snr_grid.plot()" - ] - }, - { - "cell_type": "markdown", - "id": "5f5e2dc0", - "metadata": {}, - "source": [ - "## Run the cosmic-integrator\n", - "\n", - "Finally, we can run the cosmic-integrator.\n", - "This iterates over all the binaries in the COMPAS data set and calculates the formation rate of each binary given an MSSFR, producing a formation rate distribution (FRD) as a function of redshift for each binary. Accounting for the selection effects we can then compute the detection rate for each binary.\n", - "\n", - "As each binary has a chirp-mass, we can bin all the detection rates as a into chirp-mass and redshift bins, which is called the detection matrix." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "b63ec709", - "metadata": { - "lines_to_next_cell": 0 - }, - "outputs": [ - { - "data": { - "text/html": [ - "
✔️ 1.51 s (2023-09-11T17:49:55/2023-09-11T17:49:57)
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "6a7535bfb1084f4bb7a54e3bc1de0952", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Computing detection rates: 0%| | 0/9879 [00:00✔️ 3.83 s (2023-09-11T17:49:57/2023-09-11T17:50:01)" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "cc4a8f5919654c6dac06b5b040efd1c7", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Computing detection rates: 0%| | 0/9879 [00:00" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "detection_matrix = DetectionMatrix.from_compas_output(\n", - " compas_fname, save_plots=False,\n", - " chirp_mass_bins=50, redshift_bins=100,\n", - " cosmological_parameters=dict(aSF=0.01, dSF=4.70, mu_z=-.23, sigma_z=0), sens='O1'\n", - " )\n", - "fig = detection_matrix.plot()" - ] - }, - { - "cell_type": "markdown", - "id": "945cd6d6", - "metadata": {}, - "source": [ - "The integration can be executed without binning the detection rates (this is not recommended for large data sets for memory reasons).\n" - ] - }, - { - "cell_type": "markdown", - "id": "d8a7ffad", - "metadata": {}, - "source": [ - "## Bootstrapping\n", - "\n", - "You may want to generate $N$ detection-rate matrices using bootstrap samples from the original BBH population. This can be done with: " - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "bfcda665", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
✔️ 8.39 s (2023-09-11T17:50:01/2023-09-11T17:50:09)
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7b17f56f73324f1a952965854a17eb15", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Bootstrapping rate matrices: 0%| | 0/5 [00:00" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "detection_matrix.compute_bootstrapped_rate_matrices(\n", - " bbh_population, cosmological_model=cosmological_model, snr_grid=snr_grid,\n", - " n_bootstraps=5\n", - ")\n", - "fig = detection_matrix.plot_bootstrapped_uncertainty()" - ] - }, - { - "cell_type": "markdown", - "id": "778f154c", - "metadata": {}, - "source": [ - "\n", - "## GPU usage\n", - "If you have a CUDA-enabled GPU, the cosmic-integrator will automatically use it to speed up the calculation. To check if your GPU is used, you can run the following" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "78d33951", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
✔️ 851 µs (2023-09-11T17:50:10/2023-09-11T17:50:10)
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GPU available: False\n" - ] - } - ], - "source": [ - "from compas_python_utils.cosmic_integration.binned_cosmic_integrator.gpu_utils import gpu_available\n", - "\n", - "print(f\"GPU available: {gpu_available}\")" - ] - }, - { - "cell_type": "markdown", - "id": "bd293b37", - "metadata": {}, - "source": [ - "## Acknowledgements\n", - "If you use the cosmic-integration code, please cite:\n", - "\n", - "```bib\n", - "\n", - "@ARTICLE{2018MNRAS.477.4685B,\n", - " author = {{Barrett}, Jim W. and {Gaebel}, Sebastian M. and {Neijssel}, Coenraad J. and {Vigna-G{\\'o}mez}, Alejandro and {Stevenson}, Simon and {Berry}, Christopher P.~L. and {Farr}, Will M. and {Mandel}, Ilya},\n", - " title = \"{Accuracy of inference on the physics of binary evolution from gravitational-wave observations}\",\n", - " journal = {\\mnras},\n", - " keywords = {black hole physics, gravitational waves, stars: black holes, stars: evolution, Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Solar and Stellar Astrophysics, Physics - Data Analysis, Statistics and Probability},\n", - " year = 2018,\n", - " month = jul,\n", - " volume = {477},\n", - " number = {4},\n", - " pages = {4685-4695},\n", - " doi = {10.1093/mnras/sty908},\n", - "archivePrefix = {arXiv},\n", - " eprint = {1711.06287},\n", - " primaryClass = {astro-ph.HE},\n", - " adsurl = {https://ui.adsabs.harvard.edu/abs/2018MNRAS.477.4685B},\n", - " adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n", - "}\n", - "\n", - "@ARTICLE{2019MNRAS.490.3740N,\n", - " author = {{Neijssel}, Coenraad J. and {Vigna-G{\\'o}mez}, Alejandro and {Stevenson}, Simon and {Barrett}, Jim W. and {Gaebel}, Sebastian M. and {Broekgaarden}, Floor S. and {de Mink}, Selma E. and {Sz{\\'e}csi}, Dorottya and {Vinciguerra}, Serena and {Mandel}, Ilya},\n", - " title = \"{The effect of the metallicity-specific star formation history on double compact object mergers}\",\n", - " journal = {\\mnras},\n", - " keywords = {gravitational waves, (stars:) binaries: general, stars: massive, galaxies: star formation, Astrophysics - Solar and Stellar Astrophysics, Astrophysics - Astrophysics of Galaxies},\n", - " year = 2019,\n", - " month = dec,\n", - " volume = {490},\n", - " number = {3},\n", - " pages = {3740-3759},\n", - " doi = {10.1093/mnras/stz2840},\n", - "archivePrefix = {arXiv},\n", - " eprint = {1906.08136},\n", - " primaryClass = {astro-ph.SR},\n", - " adsurl = {https://ui.adsabs.harvard.edu/abs/2019MNRAS.490.3740N},\n", - " adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n", - "}\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "63bbe6bc", - "metadata": {}, - "source": [ - "#### Older version of Cosmic-integrator\n", - "\n", - "The cosmic-integrator code has undergone a major overhaul in 2023. If you would like to use the previous version, you can find it [here](https://github.com/TeamCOMPAS/COMPAS/tree/8af87e8e84568da11133deae034e23aee92c68e9). Please let the COMPAS team know that you are using this version, so we can know that there is still interest in this version." - ] - } - ], - "metadata": { - "jupytext": { - "formats": "py:light,ipynb" - }, - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/online-docs/pages/User guide/Post-processing/notebooks/CosmicIntegration.py b/online-docs/pages/User guide/Post-processing/notebooks/CosmicIntegration.py index 375ad79cf..6dcdbaef6 100644 --- a/online-docs/pages/User guide/Post-processing/notebooks/CosmicIntegration.py +++ b/online-docs/pages/User guide/Post-processing/notebooks/CosmicIntegration.py @@ -42,20 +42,28 @@ # + import numpy as np -from compas_python_utils.cosmic_integration.binned_cosmic_integrator.bbh_population import generate_mock_bbh_population_file -from compas_python_utils.cosmic_integration.binned_cosmic_integrator.bbh_population import BBHPopulation +from compas_python_utils.cosmic_integration.binned_cosmic_integrator.binary_population import generate_mock_population +from compas_python_utils.cosmic_integration.binned_cosmic_integrator.binary_population import BinaryPopulation np.random.seed(42) -m1_min = 5 -m1_max = 150 -m2_min = 0.1 -compas_fname = generate_mock_bbh_population_file( - "mock_compas_data.h5", n_systems=int(1e4), frac_bbh=1, - m1_min=m1_min, m1_max=m1_max, m2_min=m2_min +mass_params = dict( + m1_min = 5, + m1_max = 150, + m2_min = 0.1, +) + +compas_fname = generate_mock_population( + "mock_compas_data.h5", n_systems=int(1e4), + frac_bbh=1, frac_bhns=0, frac_bns=0, + **mass_params, +) +bbh_population = BinaryPopulation.from_compas_h5( + compas_fname, + dcos_included=['BBH'], + **mass_params ) -bbh_population = BBHPopulation.from_compas_h5(compas_fname, m1_min=m1_min, m1_max=m1_max, m2_min=m2_min) fig = bbh_population.plot() # - @@ -112,7 +120,7 @@ # + from compas_python_utils.cosmic_integration.binned_cosmic_integrator.snr_grid import SNRGrid -snr_grid = SNRGrid() +snr_grid = SNRGrid(sensitivity="O3") fig = snr_grid.plot() # - @@ -135,7 +143,10 @@ # We have a helper class to do this in one go: detection_matrix = DetectionMatrix.from_compas_output( - compas_fname, save_plots=False, + compas_fname, + sens="O3", + dcos_included=["BBH"], + save_plots=False, chirp_mass_bins=50, redshift_bins=100, cosmological_parameters=dict(aSF=0.01, dSF=4.70, mu_z=-.23, sigma_z=0), ) diff --git a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst index 027362a8e..f58250522 100644 --- a/online-docs/pages/User guide/Program options/program-options-list-defaults.rst +++ b/online-docs/pages/User guide/Program options/program-options-list-defaults.rst @@ -164,15 +164,15 @@ Default = 1.0 **--common-envelope-lambda-nanjing-enhanced** |br| Continuous extrapolation beyond maximum radius range in Nanjing lambda's as implemented in StarTrack. Only used when ``--common-envelope-lambda-prescription = LAMBDA_NANJING``. |br| -Default = FALSE +Default = TRUE **--common-envelope-lambda-nanjing-interpolate-in-mass** |br| -Interpolate Nanjing lambda parameters across different mass models. Only used when ``--common-envelope-lambda-prescription = LAMBDA_NANJING``. |br| -Default = FALSE +Interpolate Nanjing lambda parameters across different mass models. Only used when ``--common-envelope-lambda-prescription = LAMBDA_NANJING``. Requires ``--common-envelope-lambda-nanjing-enhanced``. |br| +Default = TRUE **--common-envelope-lambda-nanjing-interpolate-in-metallicity** |br| -Interpolate Nanjing lambda parameters across population I and population II metallicity models. Only used when ``--common-envelope-lambda-prescription = LAMBDA_NANJING``. |br| -Default = FALSE +Interpolate Nanjing lambda parameters across population I and population II metallicity models. Only used when ``--common-envelope-lambda-prescription = LAMBDA_NANJING``. Requires ``--common-envelope-lambda-nanjing-enhanced``. |br| +Default = TRUE **--common-envelope-lambda-nanjing-use-rejuvenated-mass** |br| Use rejuvenated or effective ZAMS mass instead of true birth mass when computing Nanjing lambda parameters. Only used when ``--common-envelope-lambda-prescription = LAMBDA_NANJING``. |br| @@ -343,6 +343,7 @@ Default = 0.0 **--debug-classes** |br| Developer-defined debug classes to enable (vector). |br| +See :doc:`Vector program options <./program-options-vector-options>` for option format. |br| Default = `All debug classes enabled (e.g. no filtering)` **--debug-level** |br| @@ -397,7 +398,7 @@ Default = FALSE **--enhance-CHE-lifetimes-luminosities** |br| Enhance lifetimes and luminosities of CH stars using a fit to detailed models from Szecsi et al. (2015) -Default = FALSE +Default = TRUE **--envelope-state-prescription** |br| Prescription for determining whether the envelope of the star is convective or radiative. |br| @@ -512,26 +513,26 @@ Default = Sampled from IMF **--initial-mass-2** |br| Initial mass for the secondary star when evolving in BSE mode (:math:`M_\odot`). |br| -Default = Sampled from IMF +Default = Sampled from the mass ratio distribution specified by ``--mass-ratio-distribution`` (see also ``--mass-ratio-max``, ``-mass-ratio-min``, ``--minimum-sampled-secondary-mass``) **--initial-mass-function [ -i ]** |br| Initial mass function. |br| Options: { SALPETER, POWERLAW, UNIFORM, KROUPA } |br| -``SALPETER`` and ``KROUPA`` use the IMFs of Salpeter 1955 and Kroupa 2001 |br| -``POWERLAW`` samples from a single power law with slope ``--initial-mass-power`` |br| -``UNIFORM`` samples uniformly between ``--initial-mass-min`` and ``--initial-mass-min`` |br| +``SALPETER`` and ``KROUPA`` use the IMFs of Salpeter 1955 and Kroupa 2001, bounded by ``--initial-mass-function-min`` and ``--initial-mass-function-max`` |br| +``POWERLAW`` is a single power law with slope ``--initial-mass-function-power`` |br| +``UNIFORM`` is a uniform distribution between ``--initial-mass-function-min`` and ``--initial-mass-function-max`` |br| Default = KROUPA -**--initial-mass-max** |br| -Maximum mass to generate using given IMF (:math:`M_\odot`). |br| +**--initial-mass-function-max** |br| +The maximum mass (in Msol) to sample from the initial mass function (IMF), (only used when sampling initial mass) (:math:`M_\odot`). |br| Default = 150.0 -**--initial-mass-min** |br| -Minimum mass to generate using given IMF (:math:`M_\odot`). |br| +**--initial-mass-function-min** |br| +The minimum mass (in Msol) to sample from the initial mass function (IMF), (only used when sampling initial mass) (:math:`M_\odot`). |br| Default = 5.0 -**--initial-mass-power** |br| -Single power law power to generate primary mass using ``POWERLAW`` IMF. |br| +**--initial-mass-function-power** |br| +The power to use when using the ``POWERLAW`` IMF. |br| Default = 0.0 .. _options-props-J: @@ -585,16 +586,18 @@ Default = 0.0 **--kick-magnitude-distribution** |br| Natal kick magnitude distribution. |br| -Options: { ZERO, FIXED, FLAT, MAXWELLIAN, BRAYELDRIDGE, MULLER2016, MULLER2016MAXWELLIAN, MULLERMANDEL } |br| +Options: { ZERO, FIXED, FLAT, MAXWELLIAN, BRAYELDRIDGE, MULLER2016, MULLER2016MAXWELLIAN, MULLERMANDEL, LOGNORMAL } |br| ``ZERO`` assigns kick magnitudes of 0.0. |br| ``FIXED`` always sets the magnitude to a fixed value based on supernova type. |br| ``FLAT`` and ``MAXWELLIAN`` draw kicks from uniform or Maxwellian (e.g., Hobbs et al., 2005) distributions, respectively. |br| ``BRAYELDRIDGE`` and ``MULLERMANDEL`` use momentum-preserving kicks from Bray & Eldrigde 2018 and Mandel & Mueller 2020, respectively. |br| -``MULLER2016`` and ``MULLER2016MAXWELLIAN`` use kicks from Mueller 2016 as implemented in Vigna-Gomez et al., 2018 |br| -(reduced by a factor of sqrt(3) in the latter case). |br| Note that this is independent from ``--remnant-mass-prescription`` to provide flexibility; however, the ``MULLERMANDEL`` |br| kick prescription is intended to be consistently used with the ``MULLERMANDEL`` remnant mass prescription, |br| -as well as with the ``MALTSEV2024`` remnant mass prescription. |br| +as well as with the ``MALTSEV2024`` remnant mass prescription (e.g., if used with other remnant mass prescriptions, +inconsistent black hole kicks may be applied rescaled by fallback fractions). |br| +``MULLER2016`` and ``MULLER2016MAXWELLIAN`` use kicks from Mueller 2016 as implemented in Vigna-Gomez et al., 2018 |br| +(reduced by a factor of sqrt(3) in the latter case). |br| +``LOGNORMAL`` applies kicks drawn from the Disberg & Mandel 2025 log-normal distribution. |br| Default = MULLERMANDEL **--kick-magnitude-max** |br| @@ -694,6 +697,7 @@ Default = HURLEY_ADD |br| **--log-classes** |br| Logging classes to be enabled (vector). |br| +See :doc:`Vector program options <./program-options-vector-options>` for option format. |br| Default = `All debug classes enabled (e.g. no filtering)` **--logfile-common-envelopes** |br| @@ -709,12 +713,12 @@ Filename for logfile record definitions file. |br| Default = ’’ (None) **--logfile-detailed-output** |br| -Filename for the Detailed Output logfile. |br| +Filename for the BSE/SSE Detailed Output logfile. |br| Default = ’SSE_Detailed_Output’ for SSE mode; ’BSE_Detailed_Output’ for BSE mode |br| **--logfile-detailed-output-record-types** |br| -Enabled record types for the Detailed Output logfile. |br| -Default = -1 (all record types) |br| +Enabled record types for the BSE/SSE Detailed Output logfile. |br| +Default = 25 (record types 1, 4, and 5 (INITIAL_STATE, TIMESTEP_COMPLETED, and FINAL_STATE)) |br| **--logfile-double-compact-objects** |br| Filename for the Double Compact Objects logfile (BSE mode). |br| @@ -733,8 +737,8 @@ Filename for the Pulsar Evolution logfile (BSE mode). |br| Default = ’BSE_Pulsar_Evolution’ **--logfile-pulsar-evolution-record-types** |br| -Enabled record types for the Pulsar Evolution logfile (BSE mode). |br| -Default = -1 (all record types) |br| +Enabled record types for the BSE/SSE Pulsar Evolution logfile. |br| +Default = 4 (record types 3 ((Pulsar) TIMESTEP_COMPLETED)) |br| **--logfile-rlof-parameters** |br| Filename for the RLOF Printing logfile (BSE mode). |br| @@ -756,6 +760,14 @@ Default = -1 (all record types) |br| Filename for the Switch Log logfile. |br| Default = ’SSE_Switch_Log’ for SSE mode; ’BSE_Switch_Log’ for BSE mode |br| +**--logfile-system-snapshot-log** |br| +Filename for the System Snapshot logfile. |br| +Default = ’SSE_System_Snapshot_Log’ for SSE mode; ’BSE_System_Snapshot_Log’ for BSE mode |br| + +**--logfile-system-snapshot-log-record-types** |br| +Enabled record types for the System Snapshot logfile. |br| +Default = -1 (all record types) |br| + **--logfile-system-parameters** |br| Filename for the System Parameters logfile (BSE mode). |br| Default = ’SSE_System_Parameters’ for SSE mode; ’BSE_System_Parameters’ for BSE mode |br| @@ -800,12 +812,26 @@ Default = 4.2 **--main-sequence-core-mass-prescription** |br| Main sequence core mass prescription. |br| -Options: {ZERO, MANDEL, BRCEK} |br| -``ZERO`` : No core mass treatment, set to zero |br| +Options: {HURLEY, MANDEL, BRCEK} |br| +``HURLEY`` : Treatment from Hurley et al. (2000), in which MS stars do not have a distinct core and core evolution is not tracked |br| ``MANDEL`` : The core following case A mass transfer is set equal to the expected core mass of a newly formed HG star with mass equal to that of the donor, scaled by the fraction of the donor's MS lifetime at mass transfer |br| ``BRCEK`` : Core mass according to Shikauchi et al. (2024) with added rejuvenation of MS accretors and updated stellar tracks |br| Default = MANDEL +**--maltsev-fallback** |br| +Fixed fallback fraction when using MALTSEV2024 remnant mass prescription (must be between 0 and 1). |br| +A value of 0.0 means that fallback BHs get no fallback, only the mass of the proto-NS remnant (and will get flagged as NSs). |br| +A value of 1.0 means that fallback BHs get total fallback, taking the mass of the progenitor up to and including the He core (but not the H envelope). |br| +Default = 0.5 + +**--maltsev-mode** |br| +Choice of which variant for the MALTSEV remnant mass prescription. Variants pertain to the treatment of extrapolation at low metallicities, and are described in detail in Willcox+ 2025. |br| +Options: {OPTIMISTIC, BALANCED, PESSIMISTIC} |br| +``OPTIMISTIC`` : Compactness-peak BHs formed from the lowest CO-mass progenitors for a given metallicity. |br| +``PESSIMISTIC`` : Compactness-peak BHs only formed from the highest CO-mass progenitors for a given metallicity. |br| +``BALANCED`` : Compactness-peak BHs formed from CO-mass progenitors with masses between the two previous extremes for a given metallicity. |br| +Default = BALANCED + **--mass-change-fraction** |br| Approximate desired fractional change in stellar mass on phase when setting SSE and BSE timesteps (applied before ``--timestep--multiplier``). |br| Recommended value is 0.005. |br| @@ -814,12 +840,26 @@ Default = 0.0 **--mass-loss-prescription** |br| Mass loss prescription. |br| -Options: { ZERO, HURLEY, BELCZYNSKI2010, MERRITT2024 } |br| +Options: { ZERO, HURLEY, BELCZYNSKI2010, MERRITT2025 } |br| ``ZERO`` : No wind mass loss |br| ``HURLEY`` : Mass loss according to Hurley (2000) |br| ``BELCZYNSKI2010``: Mass loss as per Belczynski (2010), and the default prior to 2023 |br| -``MERRITT2024`` : Flexible mass loss with phase specific options: (OB, RSG, WR, VMS) |br| -Default = MERRITT2024 |br| +``MERRITT2025`` : Flexible mass loss with phase specific options: (OB, RSG, WR, VMS) |br| +Default = MERRITT2025 |br| +|br| +Note that setting this option to ``ZERO`` can have unexpected consequences, e.g., TPAGB stars that are prevented from losing +mass cannot become white dwarfs, so will become massless remnants. This is a useful option for testing, but this setting is +not recommended for production. It is better to use specific wind prescription controls, such as: |br| +|br| +``--cool-wind-mass-loss-multiplier`` |br| +``--overall-wind-mass-loss-multiplier`` |br| +``--wolf-rayet-multiplier`` |br| +``--luminous-blue-variable-multiplier`` |br| +``--LBV-mass-loss-prescription`` |br| +``--OB-mass-loss-prescription`` |br| +``--RSG-mass-loss-prescription`` |br| +``--VMS-mass-loss-prescription`` |br| +``--WR-mass-loss-prescription`` |br| **--mass-ratio [ -q ]** |br| Mass ratio :math:`\frac{m2}{m1}` used to determine secondary mass if not specified via ``--initial-mass-2``. |br| @@ -847,12 +887,12 @@ DEPRECATION NOTICE: this option has been deprecated and will soon be removed. Pl **--mass-transfer-accretion-efficiency-prescription** |br| Mass transfer accretion efficiency prescription. |br| -Options: { THERMAL, FIXED } |br| +Options: { THERMAL, FIXED, HAMSTARS } |br| Default = THERMAL **--mass-transfer-angular-momentum-loss-prescription** |br| Mass Transfer Angular Momentum Loss prescription. |br| -Options: { JEANS, ISOTROPIC, CIRCUMBINARY, MACLEOD_LINEAR, ARBITRARY } |br| +Options: { JEANS, ISOTROPIC, CIRCUMBINARY, KLENCKI_LINEAR, MACLEOD_LINEAR, ARBITRARY } |br| Default = ISOTROPIC **--mass-transfer-fa** |br| @@ -865,14 +905,14 @@ Specific angular momentum with which the non-accreted system leaves the system. Used when ``--mass-transfer-angular-momentum-loss-prescription = ARBITRARY``, ignored otherwise. |br| Default = 1.0 -**--mass-transfer-jloss-macleod-linear-fraction-degen** |br| +**--mass-transfer-jloss-linear-fraction-degen** |br| Specific angular momentum interpolation fraction for degenerate accretors, linear between 0 and 1 corresponding to the accretor and L2 point. |br| -Used when ``--mass-transfer-angular-momentum-loss-prescription = MACLEOD_LINEAR``, ignored otherwise. |br| +Used when ``--mass-transfer-angular-momentum-loss-prescription = KLENCKI_LINEAR`` or ``MACLEOD_LINEAR``, ignored otherwise. |br| Default = 0.5 -**--mass-transfer-jloss-macleod-linear-fraction-non-degen** |br| +**--mass-transfer-jloss-linear-fraction-non-degen** |br| Specific angular momentum interpolation fraction for non-degenerate accretors, linear between 0 and 1 corresponding to the accretor and L2 point. |br| -Used when ``--mass-transfer-angular-momentum-loss-prescription = MACLEOD_LINEAR``, ignored otherwise. |br| +Used when ``--mass-transfer-angular-momentum-loss-prescription = KLENCKI_LINEAR`` or ``MACLEOD_LINEAR``, ignored otherwise. |br| Default = 0.5 **--mass-transfer-rejuvenation-prescription** |br| @@ -938,9 +978,9 @@ Default = 0.03 Minimum metallicity to generate. |br| Default = 0.0001 -**--minimum-secondary-mass** |br| -Minimum mass of secondary to generate (:math:`M_\odot`). |br| -Defaults to 0.1 if ``--initial-mass-2`` is specified, otherwise value of ``--initial-mass-min``. +**--minimum-sampled-secondary-mass** |br| +Minimum mass value that can be sampled from the IMF when sampling the mass of the secondary star (:math:`M_\odot`). |br| +Default = 0.1 **--mode** |br| The mode of evolution. |br| @@ -955,8 +995,12 @@ Default = 200.0 Scaling prefactor for NS kicks when using the `MULLERMANDEL` kick magnitude distribution |br| Default = 520.0 -**--muller-mandel-sigma-kick** |br| -Scatter width for NS and BH kicks when using the `MULLERMANDEL` kick magnitude distribution |br| +**--muller-mandel-sigma-kick-BH** |br| +Scatter width for BH kicks when using the `MULLERMANDEL` kick magnitude distribution |br| +Default = 0.3 + +**--muller-mandel-sigma-kick-NS** |br| +Scatter width for NS kicks when using the `MULLERMANDEL` kick magnitude distribution |br| Default = 0.3 .. _options-props-N: @@ -992,11 +1036,13 @@ Default = SSE **--notes** |br| Annotation strings (vector). |br| -Default = "" +See :doc:`Vector program options <./program-options-vector-options>` for option format. |br| +Default = "" for each annotation **--notes-hdrs** |br| Annotations header strings (vector). |br| -Default = `No annotations` +See :doc:`Vector program options <./program-options-vector-options>` for option format. |br| +Default = `No annotation headers (no annotations)` **--number-of-systems [ -n ]** |br| The number of systems to simulate. |br| @@ -1276,7 +1322,7 @@ Default = DECIN2023 |br| **--scale-CHE-mass-loss-with-surface-helium-abundance** |br| Scale mass loss for chemically homogeneously evolving (CHE) stars with the surface helium abundance. Transition from OB to WR mass loss towards the end of the main sequence. -Default = False +Default = TRUE **--scale-terminal-wind-velocity-with-metallicity-power** |br| Scale terminal wind velocity with metallicity to this power @@ -1317,16 +1363,35 @@ Default = TRUE Enables printing of the Switch Log logfile. |br| Default = FALSE +**--system-snapshot-age-thresholds** |br| +Age thresholds for the System Snapshot logfile. This is a vector option: one or more age threshold values may be specified. |br| +See :doc:`Vector program options <./program-options-vector-options>` for option format. |br| |br| +In SSE mode, writing to the System Snapshot logfile is triggered when the age of the star exceeds any of the age thresholds set. +A record is written to the System Snapshot logfile on the first timestep at which the age threshold is exceeded. |br| |br| +In BSE mode, writing to the System Snapshot logfile is triggered when the age of either of the constituent stars exceeds any of the age thresholds set. +A record is written to the System Snapshot logfile on the first timestep at which the age threshold is exceeded. It is possible for two records to be +logged for each age threshold if the constiuent stars exceed the threshold on different timesteps. |br| |br| +Note that the age of stars may be reduced for various reasons (phase change, rejuvenation, winds/mass transfer, etc.), and if the age of a star drops below an +age threshold, another record will be logged if the star then ages beyond the same threshold (so several records might be logged for the same star crossing the +same threshold if the age of the star oscillates around the threshold). + +**--system-snapshot-time-thresholds** |br| +Time thresholds for the System Snapshot logfile. This is a vector option: one or more time threshold values may be specified. |br| +See :doc:`Vector program options <./program-options-vector-options>` for option format. |br| |br| +In SSE and BSE mode, writing to the System Snapshot logfile is triggered when the simulation time exceeds any of the time thresholds set. +A record is written to the System Snapshot logfile on the first timestep at which the simulation time threshold is exceeded. + + .. _options-props-T: :ref:`Back to Top ` **--tides-prescription** |br| Prescription for tidal evolution of the binary. |br| -Options: { NONE, PERFECT, KAPIL2024 } |br| +Options: { NONE, PERFECT, KAPIL2025 } |br| ``NONE`` disables tidal interactions. |br| ``PERFECT`` evolves the binary assuming instantaneous synchronization and circularization. |br| -``KAPIL2024`` uses the prescription from Kapil+ (2024). |br| +``KAPIL2025`` uses the prescription from Kapil+ (2025). |br| Default = NONE **--timestep-filename** |br| @@ -1334,32 +1399,42 @@ User-defined timesteps filename. (See :doc:`Timestep files <../timestep-files>`) Default = ’’ (None) **--timestep-multiplier** |br| -Multiplicative factor for timestep duration. This multiplier is applied after the timesteps are chosen using other program options -such as ``--radial-change-fraction`` and ``--mass-change-fraction``, and will therefore override expected behaviour. This option is -primarily intended for debugging/testing of convergence issues rather than for production runs. |br| - Default = 1.0 +Multiplicative factor for timestep duration. |br| +|br| +This multiplier is applied after the timesteps are chosen using other program options such as ``--radial-change-fraction`` +and ``--mass-change-fraction``, and will therefore override expected behaviour. |br| +This option can be used in conjunction with ``--timestep-multipliers``, in which case this multiplier, and the appropriate +phase-dependent multiplier (specified by ``--timestep-multipliers``) are both applied. |br| +Default = 1.0 |br| |br| +This option is primarily intended for debugging/testing of convergence issues rather than for production runs. |br| + +**--timestep-multipliers** |br| +Phase-dependent multiplicative factors for timestep duration. This is a vector option: one or more timestep multiplier values may be specified. |br| +See :doc:`Vector program options <./program-options-vector-options>` for option format. |br| +A multiplicative factor can be specified for each phase (stellar type), where the ordinal value (zero-based) of the +option value indicates the stellar type (from ``MS_LTE_07`` to ``CHEMICALLY_HOMOGENEOUS``, see stellar type list at +:doc:`../../Developer guide/Headers/typedefs-dot-h`>). |br| +|br| +This multiplier is applied after the timesteps are chosen using other program options such as ``--radial-change-fraction`` and +``--mass-change-fraction``, and will therefore override expected behaviour. |br| +This option can be used in conjunction with ``--timestep-multiplier``, in which case that multiplier, and the appropriate +phase-dependent multiplier (specified by ``--timestep-multipliers``) are both applied. |br| +Default = 1.0 for each phase (stellar type) |br| |br| +This option is primarily intended for debugging/testing of convergence issues rather than for production runs. |br| + .. _options-props-U: :ref:`Back to Top ` -**--use-mass-loss** |br| -Enable mass loss through winds. |br| -Default = TRUE -Note that setting this option to FALSE can have unexpected consequences, e.g., TPAGB stars that are prevented from losing mass -cannot become white dwarfs, so will become massless remnants. This is a useful option for testing, but this setting is not recommended -for production. It is better to use specific wind prescription controls, such as: |br| -``--cool-wind-mass-loss-multiplier`` |br| -``--LBV-mass-loss-prescription`` |br| -``--luminous-blue-variable-multiplier`` |br| -``--mass-loss-prescription`` |br| -``--overall-wind-mass-loss-multiplier`` |br| -``--wolf-rayet-multiplier`` |br| - **--use-mass-transfer** |br| Enable mass transfer. |br| Default = TRUE +**--USSN-kicks-override-mandel-muller** |br| +Use user-defined USSN kicks (as a fixed value) in lieu of the Mandel & Muller kick prescription for USSNe. |br| +Default = FALSE + .. _options-props-V: :ref:`Back to Top ` @@ -1439,9 +1514,9 @@ Go to :ref:`the top of this page ` for the full alphabetical **Initial conditions** ---initial-mass-function, --initial-mass, --initial-mass-1, --initial-mass-2, --initial-mass-min, --initial-mass-max, --initial-mass-power +--initial-mass-function, --initial-mass, --initial-mass-1, --initial-mass-2, --initial-mass-function-min, --initial-mass-function-max, --initial-mass-function-power ---mass-ratio-distribution, --mass-ratio, --mass-ratio-min, --mass-ratio-max, --minimum-secondary-mass +--mass-ratio-distribution, --mass-ratio, --mass-ratio-min, --mass-ratio-max, --minimum-sampled-secondary-mass --eccentricity-distribution, --eccentricity, --eccentricity-min, --eccentricity-max @@ -1458,9 +1533,9 @@ Go to :ref:`the top of this page ` for the full alphabetical **Stellar evolution and winds** ---use-mass-loss, --check-photon-tiring-limit, --cool-wind-mass-loss-multiplier, --luminous-blue-variable-prescription, --LBV-mass-loss-prescription +--check-photon-tiring-limit, --cool-wind-mass-loss-multiplier, --luminous-blue-variable-prescription, --LBV-mass-loss-prescription --luminous-blue-variable-multiplier, --main-sequence-core-mass-prescription, --mass-loss-prescription, --overall-wind-mass-loss-multiplier, --wolf-rayet-multiplier, ---expel-convective-envelope-above-luminosity-threshold, --luminosity-to-mass-threshold, +--expel-convective-envelope-above-luminosity-threshold, --luminosity-to-mass-threshold, --scale-CHE-mass-loss-with-surface-helium-abundance --OB-mass-loss, --OB-mass-loss-prescription, --RSG-mass-loss, --RSG-mass-loss-prescription, --VMS-mass-loss, --vms-mass-loss-prescription, --WR-mass-loss, --WR-mass-loss-prescription --chemically-homogeneous-evolution, --chemically-homogeneous-evolution-mode @@ -1479,7 +1554,7 @@ Go to :ref:`the top of this page ` for the full alphabetical --critical-mass-ratio-helium-HG-non-degenerate-accretor, --critical-mass-ratio-helium-MS-degenerate-accretor, --critical-mass-ratio-helium-MS-non-degenerate-accretor, --critical-mass-ratio-helium-giant-degenerate-accretor, --critical-mass-ratio-helium-giant-non-degenerate-accretor, --critical-mass-ratio-white-dwarf-degenerate-accretor, --critical-mass-ratio-white-dwarf-non-degenerate-accretor, --eddington-accretion-factor, --mass-transfer, --use-mass-transfer, --mass-transfer-accretion-efficiency-prescription, ---mass-transfer-angular-momentum-loss-prescription, --mass-transfer-fa, --mass-transfer-jloss, --mass-transfer-jloss-macleod-linear-fraction-degen, --mass-transfer-jloss-macleod-linear-fraction-non-degen, +--mass-transfer-angular-momentum-loss-prescription, --mass-transfer-fa, --mass-transfer-jloss, --mass-transfer-jloss-linear-fraction-degen, --mass-transfer-jloss-linear-fraction-non-degen, --mass-transfer-rejuvenation-prescription, --mass-transfer-thermal-limit-accretor, --mass-transfer-thermal-limit-accretor-multiplier, --mass-transfer-thermal-limit-C, --retain-core-mass-during-caseA-mass-transfer, --stellar-zeta-prescription, --zeta-adiabatic-arbitrary, --zeta-main-sequence, --zeta-radiative-giant-star @@ -1512,8 +1587,7 @@ Go to :ref:`the top of this page ` for the full alphabetical --kick-magnitude-distribution, --kick-magnitude-sigma-CCSN-BH, --kick-magnitude-sigma-CCSN-NS, --kick-magnitude-sigma-ECSN, --kick-magnitude-sigma-USSN, --black-hole-kicks, --black-hole-kicks-mode, --fix-dimensionless-kick-magnitude, --kick-magnitude, --kick-magnitude-1, --kick-magnitude-2, --kick-magnitude-min, --kick-magnitude-max, --kick-magnitude-random, --kick-magnitude-random-1, --kick-magnitude-random-2, --kick-scaling-factor, -muller-mandel-kick-multiplier-BH, ---muller-mandel-kick-multiplier-NS, --muller-mandel-sigma-kick - +--muller-mandel-kick-multiplier-NS, --muller-mandel-sigma-kick-BH, --muller-mandel-sigma-kick-NS, --USSN-kicks-override-mandel-muller, --kick-direction, --kick-direction-distribution, --kick-direction-power, --kick-mean-anomaly-1, --kick-mean-anomaly-2, --kick-phi-1, --kick-phi-2, --kick-theta-1, --kick-theta-2 :ref:`Back to Top ` @@ -1526,19 +1600,21 @@ Go to :ref:`the top of this page ` for the full alphabetical --emit-gravitational-radiation, --evolve-double-white-dwarfs, --evolve-main-sequence-mergers, --evolve-pulsars, --evolve-unbound-systems, --include-WD-binaries-as-DCO, --mass-change-fraction, --maximum-evolution-time, --maximum-number-timestep-iterations, ---radial-change-fraction, --random-seed, --timestep-multiplier, --timestep-filename +--radial-change-fraction, --random-seed, --timestep-multiplier, --timestep-filename, +--system-snapshot-age-thresholds, --system-snapshot-time-thresholds --fp-error-mode --grid, --grid-start-line, --grid-lines-to-process ---add-options-to-sysparms, --debug-classes, --debug-level, --debug-to-file, --detailed-output, --detailed-output, --enable-warnings, --errors-to-file, +--add-options-to-sysparms, --debug-classes, --debug-level, --debug-to-file, --detailed-output, --enable-warnings, --errors-to-file, --help, --notes, --notes-hdrs, --population-data-printing, --print-bool-as-string, --quiet, --version --log-classes, --logfile-definitions, --logfile-name-prefix, --logfile-type, --log-level, --logfile-common-envelopes, --logfile-common-envelopes-record-types, --logfile-detailed-output, --logfile-detailed-output-record-types, --logfile-double-compact-objects, --logfile-double-compact-objects-record-types, --logfile-pulsar-evolution, --logfile-pulsar-evolution-record-type, --logfile-rlof-parameters, --logfile-rlof-parameters-record-types, --logfile-supernovae, ---logfile-supernovae-record-types, --logfile-switch-log, --logfile-system-parameters, --logfile-system-parameters-record-types, --output-container, +--logfile-supernovae-record-types, --logfile-switch-log, --logfile-system-parameters, --logfile-system-parameters-record-types, --logfile-system-snapshot-log, +--logfile-system-snapshot-log-record-types, --output-container, --output-path, --rlof-printing, --store-input-files, --switch-log, --hdf5-buffer-size, --hdf5-chunk-size --create-YAML-file, YAML-template diff --git a/online-docs/pages/User guide/Program options/program-options-vector-options.rst b/online-docs/pages/User guide/Program options/program-options-vector-options.rst index 60439cf7a..8b3993a8c 100644 --- a/online-docs/pages/User guide/Program options/program-options-vector-options.rst +++ b/online-docs/pages/User guide/Program options/program-options-vector-options.rst @@ -12,6 +12,7 @@ Currently, the only vector program options are: - --log-classes - --notes-hdrs - --notes +- --timestep-multipliers The notation for vector program options provides for the specification of one or more values. e.g.:: @@ -26,8 +27,8 @@ for program options (to the command-line value, then to the COMPAS default) - le `log-class` had been left blank), and specifying an empty string ("") for a value would be ambiguous (as to whether the user wanted the option value to default, or just be an empty string). -Option values (in general, but also specifically for vector options) may not begin with the dash character ('-'), because the shell parser -will parse them as option names before passing them through to COMPAS. +Option values beyond the first value may not begin with the dash character ('-'), because the shell parser will parse them as option names +before passing them through to COMPAS (this is a Boost limitation). COMPAS imposes no limit to the length (number of characters) of an individual option values that are specified as strings, but there may be practical limits imposed by the underlying system. diff --git a/online-docs/pages/User guide/Running COMPAS/running-grid.rst b/online-docs/pages/User guide/Running COMPAS/running-grid.rst index 450f4e720..ad21ef438 100644 --- a/online-docs/pages/User guide/Running COMPAS/running-grid.rst +++ b/online-docs/pages/User guide/Running COMPAS/running-grid.rst @@ -51,7 +51,7 @@ Both option ``--grid-start-line`` and ``--grid-lines-to-process`` are ignored if Example ~~~~~~~ -We will submit a set of COMPAS runs using a grid-file ``grid_demo.txt'' +We will submit a set of COMPAS runs using a grid-file ``grid_demo.txt`` .. code-block:: COMPAS --grid grid_demo.txt diff --git a/online-docs/pages/User guide/docker.rst b/online-docs/pages/User guide/docker.rst index ca61a1f8c..9c669d86d 100644 --- a/online-docs/pages/User guide/docker.rst +++ b/online-docs/pages/User guide/docker.rst @@ -140,7 +140,7 @@ Bonus Info ---------- Dockerfile -^^^^^^^^^^ +~~~~~~~~~~ The `Dockerfile `__ defines how the docker image is constructed. @@ -159,7 +159,7 @@ The Dockerfile for COMPAS consists of 8 layers: Dockerfiles usually end with a `CMD` directive specifying the command to run when the container starts. COMPAS does not have a `CMD` directive because some users will run the executable directly, while others will use `runSubmit.py`. Makefile.docker -^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~ A separate makefile is required for Docker to: 1. Separate compiled files from source files. diff --git a/online-docs/pages/how-to-cite.rst b/online-docs/pages/how-to-cite.rst index 6688a2434..30cef9ed4 100644 --- a/online-docs/pages/how-to-cite.rst +++ b/online-docs/pages/how-to-cite.rst @@ -1,33 +1,36 @@ -Citing COMPAS -------------- - -If you use this code or parts of this code for results presented in a scientific publication, we would greatly appreciate you sending -us your paper reference and making your input settings and output data publicly available by uploading it to the COMPAS Zenodo community. - -Please also cite: - -.. _cite-compas: - - Team COMPAS: J. Riley `et al.` [:cite:year:`compas2021`]. |_| |_| |_| |_| |_| |_| :download:`Bibtex citation <../COMPAS-2021methodsPaper.bib>` - -|br| -We would also greatly appreciate an acknowledgement of the form: - -"Simulations in this paper made use of the COMPAS rapid binary population synthesis code (version x.y.z), which is freely available at -http://github.com/TeamCOMPAS/COMPAS." - -|br| -Furthermore, - -If using the COMPAS model of gravitational wave selection effects, please cite :cite:t:`Barrett2018`. - -If you use COMPAS's importance sampling algorithm STROOPWAFEL, please cite :cite:t:`Broekgaarden2019`. - -If using COMPAS's integration over cosmic star formation history, please cite :cite:t:`Neijssel2019`. - -If using the COMPAS model of (pulsational) pair instability supernova, please cite :cite:t:`Stevenson2019`. - -If evolving pulsar spins and magnetic fields with COMPAS, please cite :cite:t:`Chattopadhyay2020`. - -If you use the COMPAS model of chemically homogeneous evolution, please cite :cite:t:`Riley2021`. - +Citing COMPAS +------------- + +If you use this code or parts of this code for results presented in a scientific publication, we would greatly appreciate you sending +us your paper reference and making your input settings and output data publicly available by uploading it to the COMPAS Zenodo community. + +Please also cite: + +.. _cite-compas: + + Team COMPAS: J. Riley `et al.` [:cite:year:`compas2021`]. |_| |_| |_| |_| |_| |_| :download:`Bibtex citation <../COMPAS-2021methodsPaper.bib>` + + Team COMPAS: I. Mandel `et al.` [:cite:year:`compas2025`]. |_| |_| |_| |_| |_| |_| :download:`Bibtex citation <../COMPAS-2025methodsPaper.bib>` + + +|br| +We would also greatly appreciate an acknowledgement of the form: + +"Simulations in this paper made use of the COMPAS rapid binary population synthesis code (version x.y.z), which is freely available at +http://github.com/TeamCOMPAS/COMPAS." + +|br| +Furthermore, + +If using the COMPAS model of gravitational wave selection effects, please cite :cite:t:`Barrett2018`. + +If you use COMPAS's importance sampling algorithm STROOPWAFEL, please cite :cite:t:`Broekgaarden2019`. + +If using COMPAS's integration over cosmic star formation history, please cite :cite:t:`Neijssel2019`. + +If using the COMPAS model of (pulsational) pair instability supernova, please cite :cite:t:`Stevenson2019`. + +If evolving pulsar spins and magnetic fields with COMPAS, please cite :cite:t:`Chattopadhyay2020`. + +If you use the COMPAS model of chemically homogeneous evolution, please cite :cite:t:`Riley2021`. + diff --git a/online-docs/pages/quick-links.rst b/online-docs/pages/quick-links.rst index 371a13d1e..7383d1f59 100644 --- a/online-docs/pages/quick-links.rst +++ b/online-docs/pages/quick-links.rst @@ -6,4 +6,6 @@ Quick Links ./User guide/Program options/program-options-list-defaults ./User guide/COMPAS output/standard-logfiles-record-types + ./User guide/COMPAS output/standard-logfiles-record-specification-stellar + ./User guide/COMPAS output/standard-logfiles-record-specification-binary ./how-to-cite diff --git a/online-docs/pages/whats-new.rst b/online-docs/pages/whats-new.rst index 77f82c433..b575a4032 100644 --- a/online-docs/pages/whats-new.rst +++ b/online-docs/pages/whats-new.rst @@ -3,24 +3,138 @@ What's new Following is a brief list of important updates to the COMPAS code. A complete record of changes can be found in the file ``changelog.h``. +**03.27.02 December 16, 2025** + +* Fixed a bug in the assignment of kick direction angles +* Undid replacement of --scale-CHE-mass-loss-with-surface-helium-abundance with the more general --scale-mass-loss-with-surface-helium-abundance (see 03.26.02) + +**03.26.02 October 27, 2025** + +* Added option --USSN-kicks-override-mandel-muller ; if set to true, use user-defined USSN kicks (as a fixed value) in lieu of the Mandel & Muller kick prescription for USSNe +* Replaced --scale-CHE-mass-loss-with-surface-helium-abundance with the more general --scale-mass-loss-with-surface-helium-abundance (applies to all MS stars, not just CHE stars) + +**03.26.00 September 2, 2025** + +* Added HAMSTARS mass transfer efficiency prescription + +**03.25.00 August 19, 2025** + +* Added KLENCKI_LINEAR AM loss, which is linear in the specific AM gamma instead of the orbital separation (as in MACLEOD_LINEAR). + +This is based on the variations explored in Klencki+ 2025, and is very similar in construction to the MACLEOD_LINEAR option, +with both requiring an interpolation fraction f set by the user. Therefore, the following options are deprecated: + + * ``--mass-transfer-jloss-macleod-linear-fraction-degen`` in favor of ``--mass-transfer-jloss-linear-fraction-degen`` + * ``--mass-transfer-jloss-macleod-linear-fraction-non-degen`` in favor of ``--mass-transfer-jloss-linear-fraction-non-degen`` + +and the replacement options apply for both MACLEOD_LINEAR and KLENCKI_LINEAR. + +**03.24.00 August 19, 2025** + +* Updated Maltsev remnant mass prescription to include the 3 variants described in Willcox+ 2025 (bimodality paper). +* New related options `--maltsev-fallback` which takes a float between 0 and 1 to specify the fallback fraction, and + `--maltsev-mode` with choices `'OPTIMISTIC','PESSIMISTIC','BALANCED'` for the extrapolation prescription (see Willcox+ 2025b) + +**03.23.00 August 09, 2025** + +* The following option is now deprecated, and will be removed in 1 year: + + * ``--use-mass-loss`` in favour of ``--mass-loss-prescription`` + +Instead of using ``--use-mass-loss`` or ``--use-mass-loss true`` to enable mass loss, then specifying the mass loss prescription to be used with +``--mass-loss-prescription``, mass loss can be enabled using ``--mass-loss-prescription`` with any valid prescription (that is not ``zero``), and +disabled with ``--mass-loss-prescription zero`` instead of ``use-mass-loss false``. + +**03.22.02 August 08, 2025** + +* The following options are now deprecated, and will be removed in 1 year: + + * ``--initial-mass-min`` in favour of ``--initial-mass-function-min`` + * ``--initial-mass-max`` in favour of ``--initial-mass-function-max`` + * ``--initial-mass-power`` in favour of ``--initial-mass-function-power`` + * ``--minimum-mass-secondary`` in favour of ``--minimum-sampled-secondary-mass`` + +* The user supplied value for ``--minimum-sampled-secondary-mass`` now checked against the COMPAS values for minimum initial mass (0.00007 :math:`M_\odot`) and maximum initial mass (150.0 :math:`M_\odot`) +* The secondary mass (for BSE), whether input by user, sampled, or calculated from the primary mass and mass ratio, now checked against the COMPAS value for minimum initial mass (0.00007 :math:`M_\odot`) +* The default record types written to the SSE and BSE detailed output files now include only record types 1, 4, & 5 (INITIAL_STATE, TIMESTEP_COMPLETED, and FINAL_STATE) +* The default record types written to the SSE and BSE pulsar evolution files now includes only record type 3 ((Pulsar) TIMESTEP_COMPLETED) + +**03.22.00 July 18, 2025** + +* Changed default values of --enhance-CHE-lifetimes-luminosities and --scale-CHE-mass-loss-with-surface-helium-abundance to true +* Added options to set beta and gamma prescription for second stage of 2-stage CE (``--common-envelope-second-stage-beta``, ``--common-envelope-second-stage-gamma-prescription``) +* Fixed a bug in the calculation of zeta_equilibrium, which impacts when mass transfer is declared to proceed on a nuclear timescale (and hence how conservative it is) +* Fixed the calculation of Mandel & Muller kicks; split ``--muller-mandel-sigma-kick`` into ``--muller-mandel-sigma-kick-NS`` and ``--muller-mandel-sigma-kick-BH`` + +**03.21.00 July 17, 2025** + +* Deprecated mass loss prescription MERRITT2024 in favour of MERRITT2025 +* Added version strings for gsl, boost, and HDF5 to COMPAS splashscreen + +**03.20.06 June 25, 2025** + +* The MAXWELLIAN NS CCSN kick changed from the Hobbs value of 265 km/s to 217 km/s based on 48 younger than 10 Myr pulsars with proper motions from Disberg & Mandel (2025) sample; corrects Hobbs+ 2005 missing Jacobian +* Implemented a LOGNORMAL NS CCSN kick magnitude distribution based on Disberg & Mandel, 2025 + +**03.20.03 June 18, 2025** + +* Resolved an issue that appeared in 03.10.02 with some TPAGB stars in the ECSN range (but not satisfying ECSN conditions) exploding as core-collapse supernovae + +**03.20.01 May 26, 2025** + +* Updates to mass accretion for massive ONe WD +* Changed white dwarf mass-radius relation to use expression from Eggleton 1986, suitable for extremely low-mass white dwarfs. + +**03.20.00 May 25, 2025** + +* Replaced the name of the ``KAPIL2024`` tides prescription with ``KAPIL2025``. +* Updated the equilibrium and dynamical tides equations to match the paper. +* New outputs for BSE_DETAILED_OUTPUT from tidal evolution, including ``CIRCULARIZATION_TIMESCALE``, ``SYNCHRONIZATION_TIMESCALE_1``, ``SYNCHRONIZATION_TIMESCALE_2``, ``TIDAL_POTENTIAL_LOVE_NUMBER_22_1``, ``TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_1``, and ``TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_2`` + +**03.19.00 May 22, 2025** + +* Added functionality to create new System Snapshot logfile |br| + Writing to the System Snapshot logfile is triggered by system age and/or simulation time passing thresholds set by new program options (see below). |br| + New program options added: |br| + ``--logfile-system-snapshot-log``: specifies the name of the System Snapshot logfile (default is "[BSE/SSE]_System_Snapshot_Log") |br| + ``--logfile-system-snapshot-log-record-types``: specifies the enabled record types for System Snapshot logfile (default is all types) |br| + ``--system-snapshot-age-thresholds``: specifies the age thresholds for System Snapshot logfile |br| + ``--system-snapshot-time-thresholds``: specifies the time thresholds for System Snapshot logfile + +**03.18.02 May 1, 2025** + +* Changed default for Nanjing lambdas to use enhanced lambdas and interpolate in mass and metallicity + +**03.18.00 Apr 14, 2025** + +New command line option: + +* ``--timestep-multipliers`` to enable more granular, phase-dependent, timestep multipliers + +**03.17.03 Apr 14, 2025** + +* Neutron stars are now labelled as ``RecycledNS`` when undergoing mass transfer through common envelope (when ``--neutron-star-accretion-in-ce`` is not set to ``ZERO``). +* Removed output option ``RLOF_ONTO_NS`` as it can be retrieved from existing RLOF output info. + **03.17.00 Mar 22, 2025** -* Added ENVELOPE_STATE_PRESCRIPTION::CONVECTIVE_MASS_FRACTION (default threshold of convective envelope by mass to label envelope -convective is 0.1, can be set with --convective-envelope-mass-threshold) +* Added ``ENVELOPE_STATE_PRESCRIPTION::CONVECTIVE_MASS_FRACTION`` (default threshold of convective envelope by mass to label envelope convective is 0.1, can be set with ``--convective-envelope-mass-threshold``) * Stable mass transfer now conserves angular momentum after accounting for the rotational angular momentum lost or gained by the stars -* Imposed Keplerian rotation limit on mass-gaining stars: -* Response depends on the new --response-to-spin-up option -* default (TRANSFER_TO_ORBIT) allows the star to accrete, but excess angular momentum is deposited in the orbit -* KEPLERIAN_LIMIT forces mass transfer to become non-conservative once star (approximately) reaches super-critical rotation -* while the NO_LIMIT variation allows arbitrary super-critical accretion, to match legacy choices +* Imposed Keplerian rotation limit on mass-gaining stars: response depends on the new ``--response-to-spin-up`` option, with possible values: + * ``TRANSFER_TO_ORBIT`` (default) allows the star to accrete, but excess angular momentum is deposited in the orbit + * ``KEPLERIAN_LIMIT`` forces mass transfer to become non-conservative once star (approximately) reaches super-critical rotation + * ``NO_LIMIT`` allows arbitrary super-critical accretion, to match legacy choices **03.16.02 Mar 19, 2025** -New output options for supernova: +New output options for supernova, which allow for full characterization of the binary orientation post-SN: -* ORBITAL_ANGULAR_MOMENTUM_VECTOR_X, ORBITAL_ANGULAR_MOMENTUM_VECTOR_Y, ORBITAL_ANGULAR_MOMENTUM_VECTOR_Z, -* SYSTEMIC_VELOCITY_X, SYSTEMIC_VELOCITY_Y, SYSTEMIC_VELOCITY_Z, -* These allow for full characterization of the binary orientation post-SN +* ORBITAL_ANGULAR_MOMENTUM_VECTOR_X +* ORBITAL_ANGULAR_MOMENTUM_VECTOR_Y +* ORBITAL_ANGULAR_MOMENTUM_VECTOR_Z +* SYSTEMIC_VELOCITY_X +* SYSTEMIC_VELOCITY_Y +* SYSTEMIC_VELOCITY_Z **03.15.00 Mar 5, 2025** diff --git a/online-docs/refs.bib b/online-docs/refs.bib index 52d1e2892..24a23f046 100644 --- a/online-docs/refs.bib +++ b/online-docs/refs.bib @@ -1,376 +1,416 @@ - -@ARTICLE{compas2021, - author = {{Riley}, Jeff and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold and {Team Compas}}, - title = "{Rapid Stellar and Binary Population Synthesis with COMPAS}", - journal = {\apjs}, - keywords = {1622, 154, 1108, 162, Astrophysics - Instrumentation and Methods for Astrophysics, Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Solar and Stellar Astrophysics}, - year = 2022, - month = feb, - volume = {258}, - number = {2}, - eid = {34}, - pages = {34}, - doi = {10.3847/1538-4365/ac416c}, -archivePrefix = {arXiv}, - eprint = {2109.10352}, - primaryClass = {astro-ph.IM}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2022ApJS..258...34R}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - - -@article{Barrett2018, - title = "{Accuracy of inference on the physics of binary evolution from gravitational-wave observations}", - author = "{Barrett}, Jim W. and {Gaebel}, Sebastian M. and {Neijssel}, Coenraad J. - and Vigna-Gomez, Alejandro and Stevenson, Simon and Berry, Christopher P. L. - and Farr, Will M. and Mandel, Ilya", - journal = "Mon. Not. Roy. Astron. Soc.", - volume = "477", - year = "2018", - number = "4", - pages = "4685-4695", - doi = "10.1093/mnras/sty908", - eprint = "1711.06287", -archivePrefix = "arXiv", - primaryClass = "astro-ph.HE", - SLACcitation = "%%CITATION = ARXIV:1711.06287;%%" -} - -@article{Belczynski2008, - title = "{Compact Object Modeling with the StarTrack Population Synthesis Code}", - author = {{Belczynski}, Krzysztof and {Kalogera}, Vassiliki and {Rasio}, Frederic A. - and {Taam}, Ronald E. and {Zezas}, Andreas and {Bulik}, Tomasz and {Maccarone}, Thomas J. - and {Ivanova}, Natalia}, - journal = {\apjs}, - keywords = {binaries: close, black hole physics, stars: evolution, stars: neutron, white dwarfs, X-rays: - binaries, Astrophysics}, - year = "2008", - month = "Jan", - volume = {174}, - number = {1}, - pages = {223-260}, - doi = {10.1086/521026}, -archivePrefix = {arXiv}, - eprint = {astro-ph/0511811}, - primaryClass = {astro-ph}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2008ApJS..174..223B}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Broekgaarden2019, - title = "{STROOPWAFEL: simulating rare outcomes from astrophysical populations, with application to - gravitational-wave sources}", - author = {{Broekgaarden}, Floor S. and {Justham}, Stephen and {de Mink}, Selma E. - and {Gair}, Jonathan and {Mandel}, Ilya and {Stevenson}, Simon and {Barrett}, Jim W. - and {Vigna-G{\'o}mez}, Alejandro and {Neijssel}, Coenraad J.}, - journal = {\mnras}, - keywords = {gravitational waves, methods: statistical, software: development, binaries: general, stars: evolution, - Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Instrumentation and Methods for Astrophysics, - Astrophysics - Solar and Stellar Astrophysics, Physics - Data Analysis, Statistics and Probability}, - year = "2019", - month = "Dec", - volume = {490}, - number = {4}, - pages = {5228-5248}, - doi = {10.1093/mnras/stz2558}, -archivePrefix = {arXiv}, - eprint = {1905.00910}, - primaryClass = {astro-ph.HE}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2019MNRAS.490.5228B}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Chattopadhyay2020, - title = "{Modelling double neutron stars: radio and gravitational waves}", - author = {{Chattopadhyay}, Debatri and {Stevenson}, Simon and {Hurley}, Jarrod R. and {Rossi}, Luca J. - and {Flynn}, Chris}, - journal = {\mnras}, - keywords = {gravitational waves, stars: neutron, pulsars: general, Astrophysics - High Energy Astrophysical Phenomena, - General Relativity and Quantum Cosmology}, - year = 2020, - month = may, - volume = {494}, - number = {2}, - pages = {1587-1610}, - doi = {10.1093/mnras/staa756}, -archivePrefix = {arXiv}, - eprint = {1912.02415}, - primaryClass = {astro-ph.HE}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2020MNRAS.494.1587C}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Claeys2014, - title = {Theoretical uncertainties of the Type Ia supernova rate}, - author = {{Claeys}, JSW and {Pols}, OR and {Izzard}, RG and Vink, J and Verbunt, FWM}, - journal = {\aap}, - volume = {563}, - pages = {A83}, - year = {2014}, - publisher = {EDP Sciences} -} - -@article{Dewi2000, - title = "{On the energy equation and efficiency parameter of the common envelope evolution}", - author = "{Dewi}, Jasinta D. M. and {Tauris}, Thomas M.", - journal = "Astron. Astrophys.", - volume = "360", - year = "2000", - pages = "1043-1051", - eprint = "astro-ph/0007034", -archivePrefix = "arXiv", - primaryClass = "astro-ph", - SLACcitation = "%%CITATION = ASTRO-PH/0007034;%%" -} - -@article{Fryer2012, - title = "{Compact Remnant Mass Function: Dependence on the Explosion Mechanism and Metallicity}", - author = {{Fryer}, Chris L. and {Belczynski}, Krzysztof and {Wiktorowicz}, Grzegorz - and {Dominik}, Michal and {Kalogera}, Vicky and {Holz}, Daniel E.}, - journal = {\apj}, - keywords = {black hole physics, stars: neutron, supernovae: general, Astrophysics - Solar and Stellar Astrophysics, - Astrophysics - High Energy Astrophysical Phenomena}, - year = 2012, - month = Apr, - volume = {749}, - eid = {91}, - pages = {91}, - doi = {10.1088/0004-637X/749/1/91}, -archivePrefix = {arXiv}, - eprint = {1110.1726}, - primaryClass = {astro-ph.SR}, - adsurl = {https://ui.adsabs.harvard.edu/\#abs/2012ApJ...749...91F}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Fryer2022, - author = {{Fryer}, Chris L. and {Olejak}, Aleksandra and {Belczynski}, Krzysztof}, - title = "{The Effect of Supernova Convection On Neutron Star and Black Hole Masses}", - journal = {arXiv e-prints}, - keywords = {Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Solar and Stellar Astrophysics}, - year = 2022, - month = apr, - eid = {arXiv:2204.13025}, - pages = {arXiv:2204.13025}, -archivePrefix = {arXiv}, - eprint = {2204.13025}, - primaryClass = {astro-ph.HE}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2022arXiv220413025F}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - - -} - -@article{Hurley2000, - title = "{Comprehensive analytic formulae for stellar evolution as a function of mass and metallicity}", - author = {{Hurley}, Jarrod R. and {Pols}, Onno R. and {Tout}, Christopher A.}, - journal = {\mnras}, - keywords = {METHODS: ANALYTICAL, STARS: EVOLUTION, STARS: FUNDAMENTAL PARAMETERS, STARS: MASS-LOSS, STARS: - POPULATION II, GALAXIES: STELLAR CONTENT, Astrophysics}, - year = "2000", - month = "Jul", - volume = {315}, - number = {3}, - pages = {543-569}, - doi = {10.1046/j.1365-8711.2000.03426.x}, -archivePrefix = {arXiv}, - eprint = {astro-ph/0001295}, - primaryClass = {astro-ph}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2000MNRAS.315..543H}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@book{KernighanRitchie1978, - title = {The C Programming Language}, - author = {{Kernighan}, B. W. and {Ritchie}, D. M.}, - year = {1978}, - isbn = {0131101633}, - publisher = {Prentice-Hall, Inc.}, - address = {USA} -} - -@article{Kruckow2016, - title = "{Common envelope ejection in massive binary stars - Implications for the progenitors of GW150914 and GW151226}", - author = "{Kruckow}, M. U. and {Tauris}, T. M. and {Langer}, N. and Szecsi, D. and Marchant, P. and Podsiadlowski, Ph.", - journal = "Astron. Astrophys.", - volume = "596", - year = "2016", - pages = "A58", - doi = "10.1051/0004-6361/201629420", - eprint = "1610.04417", -archivePrefix = "arXiv", - primaryClass = "astro-ph.SR", - SLACcitation = "%%CITATION = ARXIV:1610.04417;%%" -} - -@article{Loveridge2011, - title = "{Analytical Expressions for the Envelope Binding Energy of Giants as a Function of Basic Stellar Parameters}", - author = {{Loveridge}, A.~J. and {van der Sluys}, M.~V. and {Kalogera}, V.}, - journal = {\apj}, - keywords = {binaries: close, stars: evolution, stars: fundamental parameters, stars: mass-loss, Astrophysics - Solar and - Stellar Astrophysics}, - year = "2011", - month = "Dec", - volume = {743}, - number = {1}, - eid = {49}, - pages = {49}, - doi = {10.1088/0004-637X/743/1/49}, -archivePrefix = {arXiv}, - eprint = {1009.5400}, - primaryClass = {astro-ph.SR}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2011ApJ...743...49L}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Mennekens2014, - title = "{Massive double compact object mergers: gravitational wave sources and r-process element production sites}", - author = {{Mennekens}, N. and {Vanbeveren}, D.}, - journal = {\aap}, - keywords = {binaries: close, stars: massive, Galaxy: evolution, Astrophysics - Solar and Stellar Astrophysics, - Astrophysics - Astrophysics of Galaxies}, - year = 2014, - month = Apr, - volume = {564}, - eid = {A134}, - pages = {A134}, - doi = {10.1051/0004-6361/201322198}, -archivePrefix = {arXiv}, - eprint = {1307.0959}, - adsurl = {https://ui.adsabs.harvard.edu/#abs/2014A&A...564A.134M}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Neijssel2019, - title = "{The effect of the metallicity-specific star formation history on double compact object mergers}", - author = {{Neijssel}, Coenraad J. and {Vigna-G{\'o}mez}, Alejandro and {Stevenson}, Simon - and {Barrett}, Jim W. and {Gaebel}, Sebastian M. and {Broekgaarden}, Floor S. - and {de Mink}, Selma E. and {Sz{\'e}csi}, Dorottya and {Vinciguerra}, Serena and {Mandel}, Ilya}, - journal = {\mnras}, - keywords = {gravitational waves, (stars:) binaries: general, stars: massive, galaxies: star formation, - Astrophysics - Solar and Stellar Astrophysics, Astrophysics - Astrophysics of Galaxies}, - year = "2019", - month = "Dec", - volume = {490}, - number = {3}, - pages = {3740-3759}, - doi = {10.1093/mnras/stz2840}, -archivePrefix = {arXiv}, - eprint = {1906.08136}, - primaryClass = {astro-ph.SR}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2019MNRAS.490.3740N}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Riley2021, - title = "{Chemically homogeneous evolution: a rapid population synthesis approach}", - author = "{Riley}, Jeff and {Mandel}, Ilya and {Marchant}, Pablo and Butler, Ellen and Nathaniel, Kaila and - Neijssel, Coenraad and Shortt, Spencer and Vigna-Gomez, Alejandro", - eprint = "2010.00002", -archivePrefix = "arXiv", - primaryClass = "astro-ph.SR", - doi = "10.1093/mnras/stab1291", - journal = "Mon. Not. Roy. Astron. Soc.", - volume = "505", - number = "1", - pages = "663--676", - year = "2021" -} - -@article{Soberman1997, - title = "{Stability criteria for mass transfer in binary stellar evolution.}", - author = {{Soberman}, G.~E. and {Phinney}, E.~S. and {van den Heuvel}, E.~P.~J.}, - journal = {\aap}, - keywords = {CLOSE BINARIES, TIDAL INTERACTION, MASS TRANSFER, Astrophysics}, - year = 1997, - month = nov, - volume = {327}, - pages = {620-635}, -archivePrefix = {arXiv}, - eprint = {astro-ph/9703016}, - primaryClass = {astro-ph}, - adsurl = {https://ui.adsabs.harvard.edu/abs/1997A&A...327..620S}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Stevenson2019, - title = "{The Impact of Pair-instability Mass Loss on the Binary Black Hole Mass Distribution}", - author = {{Stevenson}, Simon and {Sampson}, Matthew and {Powell}, Jade and {Vigna-G{\'o}mez}, Alejandro - and {Neijssel}, Coenraad J. and {Sz{\'e}csi}, Dorottya and {Mandel}, Ilya}, - journal = {\apj}, - keywords = {Gravitational waves, Gravitational wave astronomy, Black holes, Supernovae, 678, 675, 162, 1668, - Astrophysics - High Energy Astrophysical Phenomena, General Relativity and Quantum Cosmology}, - year = 2019, - month = sep, - volume = {882}, - number = {2}, - eid = {121}, - pages = {121}, - doi = {10.3847/1538-4357/ab3981}, -archivePrefix = {arXiv}, - eprint = {1904.02821}, - primaryClass = {astro-ph.HE}, - adsurl = {https://ui.adsabs.harvard.edu/abs/2019ApJ...882..121S}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Stevenson2017, - title = "{Formation of the first three gravitational-wave observations through isolated binary evolution}", - author = {{Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Mandel}, Ilya and {Barrett}, Jim W. - and {Neijssel}, Coenraad J. and {Perkins}, David and {de Mink}, Selma E.}, - journal = {Nature Communications}, - keywords = {Astrophysics - High Energy Astrophysical Phenomena, General Relativity and Quantum Cosmology}, - year = 2017, - month = Apr, - volume = {8}, - eid = {14906}, - pages = {14906}, - doi = {10.1038/ncomms14906}, -archivePrefix = {arXiv}, - eprint = {1704.01352}, - primaryClass = {astro-ph.HE}, - adsurl = {https://ui.adsabs.harvard.edu/\#abs/2017NatCo...814906S}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Vigna-Gomez2018, - title = "{On the formation history of Galactic double neutron stars}", - author = {{Vigna-G{\'o}mez}, Alejandro and {Neijssel}, Coenraad J. and {Stevenson}, Simon and {Barrett}, Jim W. - and {Belczynski}, Krzysztof and {Justham}, Stephen and {de Mink}, Selma E. and {M{\"u}ller}, Bernhard - and {Podsiadlowski}, Philipp and {Renzo}, Mathieu and {Sz{\'e}csi}, Dorottya and {Mandel}, Ilya}, - journal = {\mnras}, - keywords = {binaries: general, stars: neutron, pulsars: general, Astrophysics - Solar and Stellar Astrophysics, - Astrophysics - High Energy Astrophysical Phenomena}, - year = 2018, - month = Dec, - volume = {481}, - pages = {4009-4029}, - doi = {10.1093/mnras/sty2463}, -archivePrefix = {arXiv}, - eprint = {1805.07974}, - primaryClass = {astro-ph.SR}, - adsurl = {https://ui.adsabs.harvard.edu/\#abs/2018MNRAS.481.4009V}, - adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} - -@article{Webbink1984, - title = {{Double white dwarfs as progenitors of R Coronae Borealis stars and Type I supernovae}}, - author = {{Webbink}, RF}, - journal = {The Astrophysical Journal}, - volume = {277}, - pages = {355--360}, - year = {1984} -} - -@article{Xu2010, - title = {On the binding energy parameter $\lambda$ of common envelope evolution}, - author = {{Xu}, Xiao-Jie and {Li}, Xiang-Dong}, - journal = {The Astrophysical Journal}, - volume = {716}, - number = {1}, - pages = {114}, - year = {2010}, - publisher = {IOP Publishing} -} - + +@ARTICLE{compas2021, + author = {{Team COMPAS: Riley}, Jeff and {Agrawal}, Poojan and {Barrett}, Jim W. and {Boyett}, Kristan N.~K. and {Broekgaarden}, Floor S. and {Chattopadhyay}, Debatri and {Gaebel}, Sebastian M. and {Gittins}, Fabian and {Hirai}, Ryosuke and {Howitt}, George and {Justham}, Stephen and {Khandelwal}, Lokesh and {Kummer}, Floris and {Lau}, Mike Y.~M. and {Mandel}, Ilya and {de Mink}, Selma E. and {Neijssel}, Coenraad and {Riley}, Tim and {van Son}, Lieke and {Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Vinciguerra}, Serena and {Wagg}, Tom and {Willcox}, Reinhold}, + title = "{Rapid Stellar and Binary Population Synthesis with COMPAS}", + journal = {\apjs}, + keywords = {1622, 154, 1108, 162, Astrophysics - Instrumentation and Methods for Astrophysics, Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Solar and Stellar Astrophysics}, + year = 2022, + month = feb, + volume = {258}, + number = {2}, + eid = {34}, + pages = {34}, + doi = {10.3847/1538-4365/ac416c}, +archivePrefix = {arXiv}, + eprint = {2109.10352}, + primaryClass = {astro-ph.IM}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2022ApJS..258...34R}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{compas2025, + author = {{Team COMPAS: Mandel}, Ilya and {Riley}, Jeff and {Boesky}, Adam and {Brcek}, Adam and {Hirai}, Ryosuke and {Kapil}, Veome and {Lau}, Mike Y.~M. and {Merritt}, JD and {Rodr{\'\i}guez-Segovia}, Nicol{\'a}s and {Romero-Shaw}, Isobel and {Song}, Yuzhe and {Stevenson}, Simon and {Vajpeyi}, Avi and {van Son}, L.~A.~C. and {Vigna-G{\'o}mez}, Alejandro and {Willcox}, Reinhold}, + title = "{Rapid Stellar and Binary Population Synthesis with COMPAS: Methods Paper II}", + journal = {\apjs}, + keywords = {Binary stars, Stellar populations, Stellar evolution, Stellar evolutionary models, Stellar remnants, 154, 1622, 1599, 2046, 1627, Solar and Stellar Astrophysics, High Energy Astrophysical Phenomena, Instrumentation and Methods for Astrophysics}, + year = 2025, + month = sep, + volume = {280}, + number = {1}, + eid = {43}, + pages = {43}, + doi = {10.3847/1538-4365/adf8d0}, +archivePrefix = {arXiv}, + eprint = {2506.02316}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2025ApJS..280...43T}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + + +@article{Barrett2018, + title = "{Accuracy of inference on the physics of binary evolution from gravitational-wave observations}", + author = "{Barrett}, Jim W. and {Gaebel}, Sebastian M. and {Neijssel}, Coenraad J. + and Vigna-Gomez, Alejandro and Stevenson, Simon and Berry, Christopher P. L. + and Farr, Will M. and Mandel, Ilya", + journal = "Mon. Not. Roy. Astron. Soc.", + volume = "477", + year = "2018", + number = "4", + pages = "4685-4695", + doi = "10.1093/mnras/sty908", + eprint = "1711.06287", +archivePrefix = "arXiv", + primaryClass = "astro-ph.HE", + SLACcitation = "%%CITATION = ARXIV:1711.06287;%%" +} + +@article{Belczynski2008, + title = "{Compact Object Modeling with the StarTrack Population Synthesis Code}", + author = {{Belczynski}, Krzysztof and {Kalogera}, Vassiliki and {Rasio}, Frederic A. + and {Taam}, Ronald E. and {Zezas}, Andreas and {Bulik}, Tomasz and {Maccarone}, Thomas J. + and {Ivanova}, Natalia}, + journal = {\apjs}, + keywords = {binaries: close, black hole physics, stars: evolution, stars: neutron, white dwarfs, X-rays: + binaries, Astrophysics}, + year = "2008", + month = "Jan", + volume = {174}, + number = {1}, + pages = {223-260}, + doi = {10.1086/521026}, +archivePrefix = {arXiv}, + eprint = {astro-ph/0511811}, + primaryClass = {astro-ph}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2008ApJS..174..223B}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Broekgaarden2019, + title = "{STROOPWAFEL: simulating rare outcomes from astrophysical populations, with application to + gravitational-wave sources}", + author = {{Broekgaarden}, Floor S. and {Justham}, Stephen and {de Mink}, Selma E. + and {Gair}, Jonathan and {Mandel}, Ilya and {Stevenson}, Simon and {Barrett}, Jim W. + and {Vigna-G{\'o}mez}, Alejandro and {Neijssel}, Coenraad J.}, + journal = {\mnras}, + keywords = {gravitational waves, methods: statistical, software: development, binaries: general, stars: evolution, + Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Instrumentation and Methods for Astrophysics, + Astrophysics - Solar and Stellar Astrophysics, Physics - Data Analysis, Statistics and Probability}, + year = "2019", + month = "Dec", + volume = {490}, + number = {4}, + pages = {5228-5248}, + doi = {10.1093/mnras/stz2558}, +archivePrefix = {arXiv}, + eprint = {1905.00910}, + primaryClass = {astro-ph.HE}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2019MNRAS.490.5228B}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Chattopadhyay2020, + title = "{Modelling double neutron stars: radio and gravitational waves}", + author = {{Chattopadhyay}, Debatri and {Stevenson}, Simon and {Hurley}, Jarrod R. and {Rossi}, Luca J. + and {Flynn}, Chris}, + journal = {\mnras}, + keywords = {gravitational waves, stars: neutron, pulsars: general, Astrophysics - High Energy Astrophysical Phenomena, + General Relativity and Quantum Cosmology}, + year = 2020, + month = may, + volume = {494}, + number = {2}, + pages = {1587-1610}, + doi = {10.1093/mnras/staa756}, +archivePrefix = {arXiv}, + eprint = {1912.02415}, + primaryClass = {astro-ph.HE}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2020MNRAS.494.1587C}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Claeys2014, + title = {Theoretical uncertainties of the Type Ia supernova rate}, + author = {{Claeys}, JSW and {Pols}, OR and {Izzard}, RG and Vink, J and Verbunt, FWM}, + journal = {\aap}, + volume = {563}, + pages = {A83}, + year = {2014}, + publisher = {EDP Sciences} +} + +@article{Dewi2000, + title = "{On the energy equation and efficiency parameter of the common envelope evolution}", + author = "{Dewi}, Jasinta D. M. and {Tauris}, Thomas M.", + journal = "Astron. Astrophys.", + volume = "360", + year = "2000", + pages = "1043-1051", + eprint = "astro-ph/0007034", +archivePrefix = "arXiv", + primaryClass = "astro-ph", + SLACcitation = "%%CITATION = ASTRO-PH/0007034;%%" +} + +@article{Fryer2012, + title = "{Compact Remnant Mass Function: Dependence on the Explosion Mechanism and Metallicity}", + author = {{Fryer}, Chris L. and {Belczynski}, Krzysztof and {Wiktorowicz}, Grzegorz + and {Dominik}, Michal and {Kalogera}, Vicky and {Holz}, Daniel E.}, + journal = {\apj}, + keywords = {black hole physics, stars: neutron, supernovae: general, Astrophysics - Solar and Stellar Astrophysics, + Astrophysics - High Energy Astrophysical Phenomena}, + year = 2012, + month = Apr, + volume = {749}, + eid = {91}, + pages = {91}, + doi = {10.1088/0004-637X/749/1/91}, +archivePrefix = {arXiv}, + eprint = {1110.1726}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/\#abs/2012ApJ...749...91F}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Fryer2022, + author = {{Fryer}, Chris L. and {Olejak}, Aleksandra and {Belczynski}, Krzysztof}, + title = "{The Effect of Supernova Convection On Neutron Star and Black Hole Masses}", + journal = {arXiv e-prints}, + keywords = {Astrophysics - High Energy Astrophysical Phenomena, Astrophysics - Solar and Stellar Astrophysics}, + year = 2022, + month = apr, + eid = {arXiv:2204.13025}, + pages = {arXiv:2204.13025}, +archivePrefix = {arXiv}, + eprint = {2204.13025}, + primaryClass = {astro-ph.HE}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2022arXiv220413025F}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +} + +@article{Hurley2000, + title = "{Comprehensive analytic formulae for stellar evolution as a function of mass and metallicity}", + author = {{Hurley}, Jarrod R. and {Pols}, Onno R. and {Tout}, Christopher A.}, + journal = {\mnras}, + keywords = {METHODS: ANALYTICAL, STARS: EVOLUTION, STARS: FUNDAMENTAL PARAMETERS, STARS: MASS-LOSS, STARS: + POPULATION II, GALAXIES: STELLAR CONTENT, Astrophysics}, + year = "2000", + month = "Jul", + volume = {315}, + number = {3}, + pages = {543-569}, + doi = {10.1046/j.1365-8711.2000.03426.x}, +archivePrefix = {arXiv}, + eprint = {astro-ph/0001295}, + primaryClass = {astro-ph}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2000MNRAS.315..543H}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@book{KernighanRitchie1978, + title = {The C Programming Language}, + author = {{Kernighan}, B. W. and {Ritchie}, D. M.}, + year = {1978}, + isbn = {0131101633}, + publisher = {Prentice-Hall, Inc.}, + address = {USA} +} + +@article{Kruckow2016, + title = "{Common envelope ejection in massive binary stars - Implications for the progenitors of GW150914 and GW151226}", + author = "{Kruckow}, M. U. and {Tauris}, T. M. and {Langer}, N. and Szecsi, D. and Marchant, P. and Podsiadlowski, Ph.", + journal = "Astron. Astrophys.", + volume = "596", + year = "2016", + pages = "A58", + doi = "10.1051/0004-6361/201629420", + eprint = "1610.04417", +archivePrefix = "arXiv", + primaryClass = "astro-ph.SR", + SLACcitation = "%%CITATION = ARXIV:1610.04417;%%" +} + +@article{Loveridge2011, + title = "{Analytical Expressions for the Envelope Binding Energy of Giants as a Function of Basic Stellar Parameters}", + author = {{Loveridge}, A.~J. and {van der Sluys}, M.~V. and {Kalogera}, V.}, + journal = {\apj}, + keywords = {binaries: close, stars: evolution, stars: fundamental parameters, stars: mass-loss, Astrophysics - Solar and + Stellar Astrophysics}, + year = "2011", + month = "Dec", + volume = {743}, + number = {1}, + eid = {49}, + pages = {49}, + doi = {10.1088/0004-637X/743/1/49}, +archivePrefix = {arXiv}, + eprint = {1009.5400}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2011ApJ...743...49L}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Mennekens2014, + title = "{Massive double compact object mergers: gravitational wave sources and r-process element production sites}", + author = {{Mennekens}, N. and {Vanbeveren}, D.}, + journal = {\aap}, + keywords = {binaries: close, stars: massive, Galaxy: evolution, Astrophysics - Solar and Stellar Astrophysics, + Astrophysics - Astrophysics of Galaxies}, + year = 2014, + month = Apr, + volume = {564}, + eid = {A134}, + pages = {A134}, + doi = {10.1051/0004-6361/201322198}, +archivePrefix = {arXiv}, + eprint = {1307.0959}, + adsurl = {https://ui.adsabs.harvard.edu/#abs/2014A&A...564A.134M}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Neijssel2019, + title = "{The effect of the metallicity-specific star formation history on double compact object mergers}", + author = {{Neijssel}, Coenraad J. and {Vigna-G{\'o}mez}, Alejandro and {Stevenson}, Simon + and {Barrett}, Jim W. and {Gaebel}, Sebastian M. and {Broekgaarden}, Floor S. + and {de Mink}, Selma E. and {Sz{\'e}csi}, Dorottya and {Vinciguerra}, Serena and {Mandel}, Ilya}, + journal = {\mnras}, + keywords = {gravitational waves, (stars:) binaries: general, stars: massive, galaxies: star formation, + Astrophysics - Solar and Stellar Astrophysics, Astrophysics - Astrophysics of Galaxies}, + year = "2019", + month = "Dec", + volume = {490}, + number = {3}, + pages = {3740-3759}, + doi = {10.1093/mnras/stz2840}, +archivePrefix = {arXiv}, + eprint = {1906.08136}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2019MNRAS.490.3740N}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Riley2021, + title = "{Chemically homogeneous evolution: a rapid population synthesis approach}", + author = "{Riley}, Jeff and {Mandel}, Ilya and {Marchant}, Pablo and Butler, Ellen and Nathaniel, Kaila and + Neijssel, Coenraad and Shortt, Spencer and Vigna-Gomez, Alejandro", + eprint = "2010.00002", +archivePrefix = "arXiv", + primaryClass = "astro-ph.SR", + doi = "10.1093/mnras/stab1291", + journal = "Mon. Not. Roy. Astron. Soc.", + volume = "505", + number = "1", + pages = "663--676", + year = "2021" +} + +@article{Soberman1997, + title = "{Stability criteria for mass transfer in binary stellar evolution.}", + author = {{Soberman}, G.~E. and {Phinney}, E.~S. and {van den Heuvel}, E.~P.~J.}, + journal = {\aap}, + keywords = {CLOSE BINARIES, TIDAL INTERACTION, MASS TRANSFER, Astrophysics}, + year = 1997, + month = nov, + volume = {327}, + pages = {620-635}, +archivePrefix = {arXiv}, + eprint = {astro-ph/9703016}, + primaryClass = {astro-ph}, + adsurl = {https://ui.adsabs.harvard.edu/abs/1997A&A...327..620S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Stevenson2019, + title = "{The Impact of Pair-instability Mass Loss on the Binary Black Hole Mass Distribution}", + author = {{Stevenson}, Simon and {Sampson}, Matthew and {Powell}, Jade and {Vigna-G{\'o}mez}, Alejandro + and {Neijssel}, Coenraad J. and {Sz{\'e}csi}, Dorottya and {Mandel}, Ilya}, + journal = {\apj}, + keywords = {Gravitational waves, Gravitational wave astronomy, Black holes, Supernovae, 678, 675, 162, 1668, + Astrophysics - High Energy Astrophysical Phenomena, General Relativity and Quantum Cosmology}, + year = 2019, + month = sep, + volume = {882}, + number = {2}, + eid = {121}, + pages = {121}, + doi = {10.3847/1538-4357/ab3981}, +archivePrefix = {arXiv}, + eprint = {1904.02821}, + primaryClass = {astro-ph.HE}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2019ApJ...882..121S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Stevenson2017, + title = "{Formation of the first three gravitational-wave observations through isolated binary evolution}", + author = {{Stevenson}, Simon and {Vigna-G{\'o}mez}, Alejandro and {Mandel}, Ilya and {Barrett}, Jim W. + and {Neijssel}, Coenraad J. and {Perkins}, David and {de Mink}, Selma E.}, + journal = {Nature Communications}, + keywords = {Astrophysics - High Energy Astrophysical Phenomena, General Relativity and Quantum Cosmology}, + year = 2017, + month = Apr, + volume = {8}, + eid = {14906}, + pages = {14906}, + doi = {10.1038/ncomms14906}, +archivePrefix = {arXiv}, + eprint = {1704.01352}, + primaryClass = {astro-ph.HE}, + adsurl = {https://ui.adsabs.harvard.edu/\#abs/2017NatCo...814906S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Vigna-Gomez2018, + title = "{On the formation history of Galactic double neutron stars}", + author = {{Vigna-G{\'o}mez}, Alejandro and {Neijssel}, Coenraad J. and {Stevenson}, Simon and {Barrett}, Jim W. + and {Belczynski}, Krzysztof and {Justham}, Stephen and {de Mink}, Selma E. and {M{\"u}ller}, Bernhard + and {Podsiadlowski}, Philipp and {Renzo}, Mathieu and {Sz{\'e}csi}, Dorottya and {Mandel}, Ilya}, + journal = {\mnras}, + keywords = {binaries: general, stars: neutron, pulsars: general, Astrophysics - Solar and Stellar Astrophysics, + Astrophysics - High Energy Astrophysical Phenomena}, + year = 2018, + month = Dec, + volume = {481}, + pages = {4009-4029}, + doi = {10.1093/mnras/sty2463}, +archivePrefix = {arXiv}, + eprint = {1805.07974}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/\#abs/2018MNRAS.481.4009V}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Webbink1984, + title = {{Double white dwarfs as progenitors of R Coronae Borealis stars and Type I supernovae}}, + author = {{Webbink}, RF}, + journal = {The Astrophysical Journal}, + volume = {277}, + pages = {355--360}, + year = {1984} +} + +@article{Xu2010, + title = {On the binding energy parameter $\lambda$ of common envelope evolution}, + author = {{Xu}, Xiao-Jie and {Li}, Xiang-Dong}, + journal = {The Astrophysical Journal}, + volume = {716}, + number = {1}, + pages = {114}, + year = {2010}, + publisher = {IOP Publishing} +} + + +@ARTICLE{Picker2024, + author = {{Picker}, Lewis and {Hirai}, Ryosuke and {Mandel}, Ilya}, + title = "{Fits for the Convective Envelope Mass in Massive Stars}", + journal = {\apj}, + keywords = {Common envelope evolution, Common envelope binary stars, Stellar evolution, 2154, 2156, 1599, Astrophysics - Solar and Stellar Astrophysics, Astrophysics - High Energy +Astrophysical Phenomena}, + year = 2024, + month = jul, + volume = {969}, + number = {1}, + eid = {1}, + pages = {1}, + doi = {10.3847/1538-4357/ad4a5d}, +archivePrefix = {arXiv}, + eprint = {2402.13180}, + primaryClass = {astro-ph.SR}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2024ApJ...969....1P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/py_tests/conftest.py b/py_tests/conftest.py index 53053e2ed..b428fad30 100644 --- a/py_tests/conftest.py +++ b/py_tests/conftest.py @@ -4,8 +4,16 @@ import subprocess import h5py import pytest -from compas_python_utils.cosmic_integration.binned_cosmic_integrator.bbh_population import \ - generate_mock_bbh_population_file +from compas_python_utils.cosmic_integration.binned_cosmic_integrator.binary_population import \ + generate_mock_population + +# Testvalues used in test_total_mass_evolved_per_z defined in py_tests/test_values.py +from py_tests.test_values import MAKE_PLOTS, M1_MIN, M1_MAX, M2_MIN, F_BIN + + +# Testvalues used in test_total_mass_evolved_per_z defined in py_tests/test_values.py +from py_tests.test_values import MAKE_PLOTS, M1_MIN, M1_MAX, M2_MIN, F_BIN + HERE = os.path.dirname(__file__) TEST_CONFIG_DIR = os.path.join(HERE, "test_data") @@ -58,7 +66,10 @@ def get_compas_data(path: str) -> Dict[str, Any]: @pytest.fixture def fake_compas_output(tmpdir) -> str: fname = f"{tmpdir}/COMPAS_mock_output.h5" - generate_mock_bbh_population_file( + generate_mock_population( filename=fname, + m1_min=M1_MIN, + m1_max=M1_MAX, + m2_min=M2_MIN ) return fname \ No newline at end of file diff --git a/py_tests/test_binned_integrator.py b/py_tests/test_binned_integrator.py index 19afcce7d..39d04bcb6 100644 --- a/py_tests/test_binned_integrator.py +++ b/py_tests/test_binned_integrator.py @@ -1,5 +1,5 @@ from compas_python_utils.cosmic_integration.binned_cosmic_integrator.cosmological_model import CosmologicalModel -from compas_python_utils.cosmic_integration.binned_cosmic_integrator.bbh_population import BBHPopulation +from compas_python_utils.cosmic_integration.binned_cosmic_integrator.binary_population import BinaryPopulation from compas_python_utils.cosmic_integration.binned_cosmic_integrator.snr_grid import SNRGrid from compas_python_utils.cosmic_integration.binned_cosmic_integrator.conversions import * from compas_python_utils.cosmic_integration.binned_cosmic_integrator import DetectionMatrix @@ -19,10 +19,10 @@ def test_cosmological_models(test_archive_dir): def test_bbh_population(fake_compas_output): - population = BBHPopulation.from_compas_h5(fake_compas_output) - assert population.n_bbh > 2 - assert population.n_systems >= population.n_bbh + population = BinaryPopulation.from_compas_h5(fake_compas_output) + assert population.n_systems > 2 assert population.mass_evolved_per_binary > 0 + assert population.n_dcos > 0 def test_SNR_grid(test_archive_dir): snr_grid = SNRGrid() @@ -42,14 +42,14 @@ def test_conversions(): assert np.isclose(m1_new, m1) assert np.isclose(m2_new, m2) -def test_binned_cosmic_integration(fake_compas_output, test_archive_dir,): +def test_binned_cosmic_integration(fake_compas_output, test_archive_dir,): detection_matrix = DetectionMatrix.from_compas_output( fake_compas_output, outdir=test_archive_dir, save_plots=True, chirp_mass_bins=None, redshift_bins=None, n_bootstrapped_matrices=1 ) assert detection_matrix.rate_matrix.shape == (len(detection_matrix.chirp_mass_bins), len(detection_matrix.redshift_bins)) detection_matrix.save() - det_matrix_fn = glob.glob(f'{test_archive_dir}/*.h5')[0] + det_matrix_fn = glob.glob(f'{test_archive_dir}/{detection_matrix.label}.h5')[0] loaded_det_matrix = DetectionMatrix.from_h5(det_matrix_fn) assert np.allclose(detection_matrix.rate_matrix, loaded_det_matrix.rate_matrix) loaded_det_matrix.bin_data(mc_bins=50, z_bins=100) diff --git a/py_tests/test_total_mass_evolved_per_z.py b/py_tests/test_total_mass_evolved_per_z.py index 8f77ec4ed..d15cffafc 100644 --- a/py_tests/test_total_mass_evolved_per_z.py +++ b/py_tests/test_total_mass_evolved_per_z.py @@ -2,19 +2,19 @@ IMF, get_COMPAS_fraction, analytical_star_forming_mass_per_binary_using_kroupa_imf, star_forming_mass_per_binary, inverse_sample_IMF, ) -from compas_python_utils.cosmic_integration.binned_cosmic_integrator.bbh_population import \ - generate_mock_bbh_population_file +from compas_python_utils.cosmic_integration.binned_cosmic_integrator.binary_population import \ + generate_mock_population import numpy as np + +from py_tests.conftest import test_archive_dir, fake_compas_output + import matplotlib.pyplot as plt import h5py as h5 import pytest -MAKE_PLOTS = False - -M1_MIN = 5 -M1_MAX = 150 -M2_MIN = 0.1 +# Testvalues defined in py_tests/test_values.py +from py_tests.test_values import MAKE_PLOTS, M1_MIN, M1_MAX, M2_MIN, F_BIN def test_imf(test_archive_dir): @@ -51,50 +51,53 @@ def test_compas_fraction(): def test_analytical_function(): - default_case = analytical_star_forming_mass_per_binary_using_kroupa_imf( - m1_max=150, - m1_min=5, - m2_min=0.1, - fbin=1 + result = analytical_star_forming_mass_per_binary_using_kroupa_imf( + m1_min=M1_MIN, + m1_max=M1_MAX, + m2_min=M2_MIN, + fbin=F_BIN ) - assert 79.0 < default_case < 79.2 + assert result > 0 def test_analytical_vs_numerical_star_forming_mass_per_binary(fake_compas_output, tmpdir, test_archive_dir): np.random.seed(42) - m1_max = M1_MAX m1_min = M1_MIN + m1_max = M1_MAX m2_min = M2_MIN - fbin = 1 + fbin = F_BIN - numerical = star_forming_mass_per_binary(fake_compas_output, m1_min, m1_max, m2_min, fbin) analytical = analytical_star_forming_mass_per_binary_using_kroupa_imf(m1_min, m1_max, m2_min, fbin) - + numerical = star_forming_mass_per_binary(fake_compas_output, m1_min, m1_max, m2_min, fbin) + assert numerical > 0 assert analytical > 0 assert np.isclose(numerical, analytical, rtol=1) if MAKE_PLOTS: fig = plot_star_forming_mass_per_binary_comparison(tmpdir, analytical, m1_min, m1_max, m2_min, fbin) - fig.savefig(f"{test_archive_dir}/analytical_vs_numerical.png") + fig.savefig(f"{test_archive_dir}/analytical_vs_numerical_var.png") def plot_star_forming_mass_per_binary_comparison( tmpdir, analytical, m1_min, m1_max, m2_min, fbin, nreps=5, nsamps=5 ): - plt.axhline(analytical, color='tab:blue', label="analytical", ls='--') + # Analytical values + plt.axhline(analytical, color='tab:blue', label=f"analytical fbin = {fbin}", ls='--') + + # Compute numerical values n_samps = np.geomspace(1e3, 5e4, nsamps) numerical_vals = [] for _ in range(nreps): vals = np.zeros(len(n_samps)) for i, n in enumerate(n_samps): fname = f"{tmpdir}/test_{i}.h5" - generate_mock_bbh_population_file(tmpdir, n_systems=int(n)) + generate_mock_population(fname, n_systems=int(n), m1_min=m1_min, m1_max=m1_max, m2_min=m2_min) vals[i] = (star_forming_mass_per_binary(fname, m1_min, m1_max, m2_min, fbin)) numerical_vals.append(vals) - # plot the upper and lower bounds of the numerical values + # plot the upper and lower bounds of the numerical values numerical_vals = np.array(numerical_vals) lower = np.percentile(numerical_vals, 5, axis=0) upper = np.percentile(numerical_vals, 95, axis=0) @@ -103,9 +106,12 @@ def plot_star_forming_mass_per_binary_comparison( linewidth=0 ) plt.plot(n_samps, np.median(numerical_vals, axis=0), color='tab:orange', label="numerical") + plt.text(n_samps[-1], np.median(numerical_vals, axis=0)[-1]*0.9, f"median numerical = {np.round(np.median(numerical_vals, axis=0)[-1],3)}", va='bottom', ha='right', color='tab:orange') + plt.text(n_samps[-1], analytical*1.1, f"analytical = {np.round(analytical,3)}", va='bottom', ha='right', color='tab:blue') plt.xscale("log") - plt.ylabel("Star forming mass per binary [M$_{\odot}$]") + plt.ylabel(r"Star forming mass per binary [M$_{\odot}$]") plt.xlabel("Number of samples") + plt.ylim(bottom=10) plt.xlim(min(n_samps), max(n_samps)) plt.legend() return plt.gcf() diff --git a/py_tests/test_values.py b/py_tests/test_values.py new file mode 100644 index 000000000..3a64f7590 --- /dev/null +++ b/py_tests/test_values.py @@ -0,0 +1,6 @@ +# Testvalues used in test_total_mass_evolved_per_z.py +MAKE_PLOTS = True +M1_MIN = 5 +M1_MAX = 100 +M2_MIN = 0.1 +F_BIN = None # None = variable f_bin, otherwise fixed value \ No newline at end of file diff --git a/src/BaseBinaryStar.cpp b/src/BaseBinaryStar.cpp index a5ec24629..0f6a1cc78 100644 --- a/src/BaseBinaryStar.cpp +++ b/src/BaseBinaryStar.cpp @@ -28,10 +28,8 @@ BaseBinaryStar::BaseBinaryStar(const unsigned long int p_Seed, const long int p_ // check that the constituent stars are not touching // also check m2 > m2min - bool done = false; - bool merger = false; - bool rlof = false; - bool secondarySmallerThanMinimumMass = false; + bool done = false; + bool rlof = false; // determine if any if the initial conditions are sampled // we consider eccentricity distribution = ECCENTRICITY_DISTRIBUTION::ZERO to be not sampled! @@ -203,16 +201,15 @@ BaseBinaryStar::BaseBinaryStar(const unsigned long int p_Seed, const long int p_ m_Star1->SetCompanion(m_Star2); m_Star2->SetCompanion(m_Star1); - merger = (m_SemiMajorAxis * AU_TO_RSOL) < (m_Star1->Radius() + m_Star2->Radius()); - secondarySmallerThanMinimumMass = utils::Compare(mass2, OPTIONS->MinimumMassSecondary()) < 0; - // check whether our initial conditions are good // if they are - evolve the binary // if they are not ok: // - if we sampled at least one of them, sample again // - if all were user supplied, set error - Evolve() will show the error and return without evolving - bool ok = !((!OPTIONS->AllowRLOFAtBirth() && rlof) || (!OPTIONS->AllowTouchingAtBirth() && merger) || secondarySmallerThanMinimumMass); + bool ok = !((!OPTIONS->AllowRLOFAtBirth() && rlof) || // rolf? + (!OPTIONS->AllowTouchingAtBirth() && (m_SemiMajorAxis * AU_TO_RSOL) < (m_Star1->Radius() + m_Star2->Radius())) || // merger? + (utils::Compare(mass2, MINIMUM_INITIAL_MASS) < 0)); // M2 < minimum? done = ok; if (!sampled && !ok) { @@ -226,7 +223,7 @@ BaseBinaryStar::BaseBinaryStar(const unsigned long int p_Seed, const long int p_ if (error != ERROR::NONE) { // ok? m_EvolutionStatus = EVOLUTION_STATUS::BINARY_ERROR; // set evolutionary status - (void)PrintBinarySystemParameters(); // no - print (log) binary system parameters + (void)PrintSystemParameters(); // no - print (log) binary system parameters THROW_ERROR(error); // throw error - can't return it... } else { // yes - ok @@ -365,6 +362,7 @@ void BaseBinaryStar::SetRemainingValues() { m_CEDetails.postCEE.rocheLobe1to2 = DEFAULT_INITIAL_DOUBLE_VALUE; m_CEDetails.postCEE.rocheLobe2to1 = DEFAULT_INITIAL_DOUBLE_VALUE; m_CEDetails.postCEE.semiMajorAxis = DEFAULT_INITIAL_DOUBLE_VALUE; + m_CEDetails.postCEE.semiMajorAxisAfterStage1 = DEFAULT_INITIAL_DOUBLE_VALUE; m_CEDetails.preCEE.eccentricity = DEFAULT_INITIAL_DOUBLE_VALUE; m_CEDetails.preCEE.rocheLobe1to2 = DEFAULT_INITIAL_DOUBLE_VALUE; m_CEDetails.preCEE.rocheLobe2to1 = DEFAULT_INITIAL_DOUBLE_VALUE; @@ -397,9 +395,10 @@ void BaseBinaryStar::SetRemainingValues() { m_PhiE = DEFAULT_INITIAL_DOUBLE_VALUE; m_PsiE = DEFAULT_INITIAL_DOUBLE_VALUE; - m_SynchronizationTimescale = DEFAULT_INITIAL_DOUBLE_VALUE; + m_SynchronizationTimescale1 = DEFAULT_INITIAL_DOUBLE_VALUE; + m_SynchronizationTimescale2 = DEFAULT_INITIAL_DOUBLE_VALUE; m_CircularizationTimescale = DEFAULT_INITIAL_DOUBLE_VALUE; - + // RLOF details m_RLOFDetails.experiencedRLOF = false; m_RLOFDetails.immediateRLOFPostCEE = false; @@ -472,6 +471,12 @@ void BaseBinaryStar::SetRemainingValues() { m_RLOFDetails.propsPostMT = &m_RLOFDetails.props1; m_RLOFDetails.propsPreMT = &m_RLOFDetails.props2; + // thresholds flags for system snapshot file + if (OPTIONS->SystemSnapshotAgeThresholds().size() > 0) { + m_SystemSnapshotAgeFlags1.assign(OPTIONS->SystemSnapshotAgeThresholds().size(), -1.0); + m_SystemSnapshotAgeFlags2.assign(OPTIONS->SystemSnapshotAgeThresholds().size(), -1.0); + } + if (OPTIONS->SystemSnapshotTimeThresholds().size() > 0) m_SystemSnapshotTimeFlags.assign(OPTIONS->SystemSnapshotTimeThresholds().size(), false); // pointers @@ -567,6 +572,10 @@ COMPAS_VARIABLE BaseBinaryStar::BinaryPropertyValue(const T_ANY_PROPERTY p_Prope case BINARY_PROPERTY::RLOF_POST_MT_STAR2_MASS: value = RLOFDetails().propsPostMT->mass2; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR1_RADIUS: value = RLOFDetails().propsPostMT->radius1; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR2_RADIUS: value = RLOFDetails().propsPostMT->radius2; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR1_TEFF: value = RLOFDetails().propsPostMT->temperature1; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR2_TEFF: value = RLOFDetails().propsPostMT->temperature2; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR1_LUM: value = RLOFDetails().propsPostMT->luminosity1; break; + case BINARY_PROPERTY::RLOF_POST_MT_STAR2_LUM: value = RLOFDetails().propsPostMT->luminosity2; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR1_RLOF: value = RLOFDetails().propsPostMT->isRLOF1; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR2_RLOF: value = RLOFDetails().propsPostMT->isRLOF2; break; case BINARY_PROPERTY::RLOF_POST_MT_STAR1_STELLAR_TYPE: value = RLOFDetails().propsPostMT->stellarType1; break; @@ -581,6 +590,10 @@ COMPAS_VARIABLE BaseBinaryStar::BinaryPropertyValue(const T_ANY_PROPERTY p_Prope case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_MASS: value = RLOFDetails().propsPreMT->mass2; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RADIUS: value = RLOFDetails().propsPreMT->radius1; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RADIUS: value = RLOFDetails().propsPreMT->radius2; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_TEFF: value = RLOFDetails().propsPreMT->temperature1; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_TEFF: value = RLOFDetails().propsPreMT->temperature2; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_LUM: value = RLOFDetails().propsPreMT->luminosity1; break; + case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_LUM: value = RLOFDetails().propsPreMT->luminosity2; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RLOF: value = RLOFDetails().propsPreMT->isRLOF1; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RLOF: value = RLOFDetails().propsPreMT->isRLOF2; break; case BINARY_PROPERTY::RLOF_PRE_MT_STAR1_STELLAR_TYPE: value = RLOFDetails().propsPreMT->stellarType1; break; @@ -601,6 +614,8 @@ COMPAS_VARIABLE BaseBinaryStar::BinaryPropertyValue(const T_ANY_PROPERTY p_Prope case BINARY_PROPERTY::SEMI_MAJOR_AXIS_AT_DCO_FORMATION: value = SemiMajorAxisAtDCOFormation(); break; case BINARY_PROPERTY::SEMI_MAJOR_AXIS_INITIAL: value = SemiMajorAxisInitial(); break; case BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE: value = SemiMajorAxisPostCEE(); break; + case BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_STAGE_1_CE: value = SemiMajorAxisAfterStage1CEE(); + break; case BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_SUPERNOVA: value = SemiMajorAxisPreSN(); break; case BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_SUPERNOVA_RSOL: value = SemiMajorAxisPreSN() * AU_TO_RSOL; break; case BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE: value = SemiMajorAxisPreCEE(); break; @@ -622,11 +637,36 @@ COMPAS_VARIABLE BaseBinaryStar::BinaryPropertyValue(const T_ANY_PROPERTY p_Prope case BINARY_PROPERTY::STELLAR_TYPE_NAME_2_PRE_COMMON_ENVELOPE: value = STELLAR_TYPE_LABEL.at(StellarType2PreCEE()); break; case BINARY_PROPERTY::SUPERNOVA_ORBIT_INCLINATION_ANGLE: value = SN_OrbitInclinationAngle(); break; case BINARY_PROPERTY::SUPERNOVA_STATE: value = SN_State(); break; - case BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE: value = SynchronizationTimescale(); break; + case BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE_1: value = SynchronizationTimescale1(); break; + case BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE_2: value = SynchronizationTimescale2(); break; case BINARY_PROPERTY::SYSTEMIC_SPEED: value = SystemicSpeed(); break; case BINARY_PROPERTY::SYSTEMIC_VELOCITY_X: value = SystemicVelocityX(); break; case BINARY_PROPERTY::SYSTEMIC_VELOCITY_Y: value = SystemicVelocityY(); break; case BINARY_PROPERTY::SYSTEMIC_VELOCITY_Z: value = SystemicVelocityZ(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_1: std::tie(value, std::ignore, std::ignore, std::ignore) = ImKnm1_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_1: std::tie(std::ignore, value, std::ignore, std::ignore) = ImKnm1_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_1: std::tie(std::ignore, std::ignore, value, std::ignore) = ImKnm1_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_1: std::tie(std::ignore, std::ignore, std::ignore, value) = ImKnm1_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_2: std::tie(value, std::ignore, std::ignore, std::ignore) = ImKnm2_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_2: std::tie(std::ignore, value, std::ignore, std::ignore) = ImKnm2_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_2: std::tie(std::ignore, std::ignore, value, std::ignore) = ImKnm2_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_2: std::tie(std::ignore, std::ignore, std::ignore, value) = ImKnm2_tidal(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_1: std::tie(value, std::ignore, std::ignore, std::ignore) = ImKnm1_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_1: std::tie(std::ignore, value, std::ignore, std::ignore) = ImKnm1_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_1: std::tie(std::ignore, std::ignore, value, std::ignore) = ImKnm1_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_1: std::tie(std::ignore, std::ignore, std::ignore, value) = ImKnm1_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_2: std::tie(value, std::ignore, std::ignore, std::ignore) = ImKnm2_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_2: std::tie(std::ignore, value, std::ignore, std::ignore) = ImKnm2_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_2: std::tie(std::ignore, std::ignore, value, std::ignore) = ImKnm2_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_2: std::tie(std::ignore, std::ignore, std::ignore, value) = ImKnm2_tidal_eq(); break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_1: std::tie(value, std::ignore, std::ignore, std::ignore) = ImKnm1_tidal_dyn();break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_1: std::tie(std::ignore, value, std::ignore, std::ignore) = ImKnm1_tidal_dyn();break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_1: std::tie(std::ignore, std::ignore, value, std::ignore) = ImKnm1_tidal_dyn();break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_1: std::tie(std::ignore, std::ignore, std::ignore, value) = ImKnm1_tidal_dyn();break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_2: std::tie(value, std::ignore, std::ignore, std::ignore) = ImKnm2_tidal_dyn();break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_2: std::tie(std::ignore, value, std::ignore, std::ignore) = ImKnm2_tidal_dyn();break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_2: std::tie(std::ignore, std::ignore, value, std::ignore) = ImKnm2_tidal_dyn();break; + case BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_2: std::tie(std::ignore, std::ignore, std::ignore, value) = ImKnm2_tidal_dyn();break; case BINARY_PROPERTY::TIME: value = Time(); break; case BINARY_PROPERTY::TIME_TO_COALESCENCE: value = TimeToCoalescence(); break; case BINARY_PROPERTY::TOTAL_ANGULAR_MOMENTUM: value = TotalAngularMomentum(); break; @@ -874,6 +914,10 @@ void BaseBinaryStar::StashRLOFProperties(const MT_TIMING p_Which) { rlofPropertiesToReset->massLossRateFromDonor = m_MassLossRateInRLOF; rlofPropertiesToReset->accretionEfficiency = m_FractionAccreted; rlofPropertiesToReset->massTransferTimescale = m_MassTransferTimescale; + rlofPropertiesToReset->temperature1 = m_Star1->Temperature(); + rlofPropertiesToReset->temperature2 = m_Star2->Temperature(); + rlofPropertiesToReset->luminosity1 = m_Star1->Luminosity(); + rlofPropertiesToReset->luminosity2 = m_Star2->Luminosity(); } @@ -891,10 +935,10 @@ void BaseBinaryStar::StashRLOFProperties(const MT_TIMING p_Which) { * const double p_RocheLobe1to2, * const double p_RocheLobe2to1) * - * @param [IN] p_SemiMajorAxis pre CEE semi-major axis in AU + * @param [IN] p_SemiMajorAxis pre CEE semi-major axis in Rsol * @param [IN] p_Eccentricity pre CEE eccentricity - * @param [IN] p_RocheLobe1to2 pre CEE Roche Lobe radius in AU as seen by star1 - * @param [IN] p_RocheLobe2to1 pre CEE Roche Lobe radius in AU as seen by star2 + * @param [IN] p_RocheLobe1to2 pre CEE Roche Lobe radius in Rsol as seen by star1 + * @param [IN] p_RocheLobe2to1 pre CEE Roche Lobe radius in Rsol as seen by star2 */ void BaseBinaryStar::SetPreCEEValues(const double p_SemiMajorAxis, const double p_Eccentricity, @@ -913,27 +957,32 @@ void BaseBinaryStar::SetPreCEEValues(const double p_SemiMajorAxis, * * m_CommonEnvelopeDetails.postCEE.eccentricity * m_CommonEnvelopeDetails.postCEE.semiMajorAxis + * m_CommonEnvelopeDetails.postCEE.semiMajorAxisAfterStage1 * m_CommonEnvelopeDetails.postCEE.rocheLobe1to2 * m_CommonEnvelopeDetails.postCEE.rocheLobe2to1 * m_RLOFDetails.immediateRLOFPostCEE * * * void SetPostCEEValues(const double p_SemiMajorAxis, + * const double p_SemiMajorAxisAfterStage1, * const double p_Eccentricity, * const double p_RocheLobe1to2, * const double p_RocheLobe2to1) * - * @param [IN] p_SemiMajorAxis post CEE semi-major axis in AU + * @param [IN] p_SemiMajorAxis post CEE semi-major axis in Rsol + * @param [IN] p_SemiMajorAxisAfterStage1 semi-major axis in Rsol after step 1 of 2-stage CE (should be 0.0 for alpha-lambda CE) * @param [IN] p_Eccentricity post CEE eccentricity - * @param [IN] p_RocheLobe1to2 post CEE Roche Lobe radius in AU as seen by star1 - * @param [IN] p_RocheLobe2to1 post CEE Roche Lobe radius in AU as seen by star2 + * @param [IN] p_RocheLobe1to2 post CEE Roche Lobe radius in Rsol as seen by star1 + * @param [IN] p_RocheLobe2to1 post CEE Roche Lobe radius in Rsol as seen by star2 */ void BaseBinaryStar::SetPostCEEValues(const double p_SemiMajorAxis, + const double p_SemiMajorAxisAfterStage1, const double p_Eccentricity, const double p_RocheLobe1to2, const double p_RocheLobe2to1) { m_CEDetails.postCEE.semiMajorAxis = p_SemiMajorAxis; + m_CEDetails.postCEE.semiMajorAxisAfterStage1 = p_SemiMajorAxisAfterStage1; m_CEDetails.postCEE.eccentricity = p_Eccentricity; m_CEDetails.postCEE.rocheLobe1to2 = p_RocheLobe1to2; m_CEDetails.postCEE.rocheLobe2to1 = p_RocheLobe2to1; @@ -1031,20 +1080,20 @@ void BaseBinaryStar::ResolveCoalescence() { * Zahn, 1977, Eq. (3.7) * * - * double CalculateDEccentricityTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star) + * double CalculateDEccentricityTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star) * - * @param [IN] p_ImKlm Imaginary [(1,0), (1,2), (2,2), (3,2)] components of the potential tidal Love number of star (unitless) + * @param [IN] p_ImKnm Imaginary [(1,0), (1,2), (2,2), (3,2)] components of the potential tidal Love number of star (unitless) * @param [IN] p_Star Star for which impact on eccentricity is to be calculated * @return Change in Eccentricity for binary (1/yr) */ -double BaseBinaryStar::CalculateDEccentricityTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star) { +double BaseBinaryStar::CalculateDEccentricityTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star) { double massStar = p_Star->Mass(); double radiusStar = p_Star->Radius(); double massCompanion = p_Star == m_Star1 ? m_Star2->Mass() : m_Star1->Mass(); double ImK10, ImK12, ImK22, ImK32; - std::tie(ImK10, ImK12, ImK22, ImK32) = p_ImKlm; + std::tie(ImK10, ImK12, ImK22, ImK32) = p_ImKnm; double R1_AU = radiusStar * RSOL_TO_AU; double R1_over_a = R1_AU / m_SemiMajorAxis; @@ -1060,20 +1109,20 @@ double BaseBinaryStar::CalculateDEccentricityTidalDt(const DBL_DBL_DBL_DBL p_ImK * Zahn, 1977, Eq. (3.8) * * - * double CalculateDOmegaTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star) + * double CalculateDOmegaTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star) * - * @param [IN] p_ImKlm Imaginary [(1,0), (1,2), (2,2), (3,2)] components of the potential tidal Love number of star (unitless) + * @param [IN] p_ImKnm Imaginary [(1,0), (1,2), (2,2), (3,2)] components of the potential tidal Love number of star (unitless) * @param [IN] p_Star Star for which impact on spin is to be calculated * @return Change in Omega for star (1/yr/yr) */ -double BaseBinaryStar::CalculateDOmegaTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star) { +double BaseBinaryStar::CalculateDOmegaTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star) { double MoIstar = p_Star->CalculateMomentOfInertiaAU(); double radiusStar = p_Star->Radius(); double massCompanion = p_Star == m_Star1 ? m_Star2->Mass() : m_Star1->Mass(); double ImK10, ImK12, ImK22, ImK32; - std::tie(ImK10, ImK12, ImK22, ImK32) = p_ImKlm; + std::tie(ImK10, ImK12, ImK22, ImK32) = p_ImKnm; double R1_AU = radiusStar * RSOL_TO_AU; double R1_over_a = R1_AU / m_SemiMajorAxis; @@ -1091,20 +1140,20 @@ double BaseBinaryStar::CalculateDOmegaTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, con * Zahn, 1977, Eq. (3.6) * * - * double CalculateDSemiMajorAxisTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star) + * double CalculateDSemiMajorAxisTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star) * - * @param [IN] p_ImKlm Imaginary [(1,0), (1,2), (2,2), (3,2)] components of the potential tidal Love number of star (unitless) + * @param [IN] p_ImKnm Imaginary [(1,0), (1,2), (2,2), (3,2)] components of the potential tidal Love number of star (unitless) * @param [IN] p_Star Star for which impact on semi-major axis is to be calculated * @return Change in semi-major axis for binary (AU/yr) */ -double BaseBinaryStar::CalculateDSemiMajorAxisTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star) { +double BaseBinaryStar::CalculateDSemiMajorAxisTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star) { double massStar = p_Star->Mass(); double radiusStar = p_Star->Radius(); double massCompanion = p_Star == m_Star1 ? m_Star2->Mass() : m_Star1->Mass(); double ImK10, ImK12, ImK22, ImK32; - std::tie(ImK10, ImK12, ImK22, ImK32) = p_ImKlm; + std::tie(ImK10, ImK12, ImK22, ImK32) = p_ImKnm; double R1_AU = radiusStar * RSOL_TO_AU; double R1_over_a = R1_AU / m_SemiMajorAxis; @@ -1492,6 +1541,8 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { double omegaSpin1_pre_CE = m_Star1->Omega(); // star1 spin (before CEE) double omegaSpin2_pre_CE = m_Star2->Omega(); // star2 spin (before CEE) + double semiMajorAxisAfterStage1 = 0.0; // semi-major axis after stage 1 (to remain zero unless using 2-stage CE formalism) + bool isDonorMS = false; // check for main sequence donor if (OPTIONS->AllowMainSequenceStarToSurviveCommonEnvelope()) { // allow main sequence stars to survive CEE? if (m_Star1->IsOneOf(ALL_MAIN_SEQUENCE)) { // yes - star1 MS_LTE_07, MS_GT_07, CHEMICALLY_HOMOGENEOUS or NAKED_HELIUM_STAR_MS? @@ -1538,7 +1589,7 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { m_Star2->SetPreCEEValues(); // squirrel away pre CEE stellar values for star 2 SetPreCEEValues(semiMajorAxisRsol, eccentricity, rRLd1Rsol, rRLd2Rsol); // squirrel away pre CEE binary values - m_MassTransferTimescale = MASS_TRANSFER_TIMESCALE::CE; + m_MassTransferTimescale = MT_TIMESCALE::CE; m_MassLossRateInRLOF = DBL_MAX; // double common envelope phase prescription (Brown 1995) to calculate new semi-major axis @@ -1584,8 +1635,8 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { // stage 1: convective envelope removal on a dynamical timescale; assumes lambda = lambda_He (this still uses the Picker convective envelope mass fit to estimate lambda) - double lambda1 = m_Star1->CalculateConvectiveEnvelopeLambdaPicker(convectiveEnvelopeMass1, maxConvectiveEnvelopeMass1); - double lambda2 = m_Star2->CalculateConvectiveEnvelopeLambdaPicker(convectiveEnvelopeMass2, maxConvectiveEnvelopeMass2); + double lambda1 = m_Star1->CalculateConvectiveEnvelopeLambdaPicker(std::tie(convectiveEnvelopeMass1, maxConvectiveEnvelopeMass1)); + double lambda2 = m_Star2->CalculateConvectiveEnvelopeLambdaPicker(std::tie(convectiveEnvelopeMass2, maxConvectiveEnvelopeMass2)); double k1 = m_Star1->IsOneOf(COMPACT_OBJECTS) ? 0.0 : (2.0 / (lambda1 * alphaCE)) * m_Star1->Mass() * (mass1 - endOfFirstStageMass1) / m_Star1->Radius(); double k2 = m_Star2->IsOneOf(COMPACT_OBJECTS) ? 0.0 : (2.0 / (lambda2 * alphaCE)) * m_Star2->Mass() * (mass2 - endOfFirstStageMass2) / m_Star2->Radius(); @@ -1594,27 +1645,28 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { double aFinalRsol = k4 / (k1 + k2 + k3); m_SemiMajorAxis = aFinalRsol * RSOL_TO_AU; + semiMajorAxisAfterStage1 = m_SemiMajorAxis; // stage 2: radiative envelope removal on a thermal timescale; assumed to be fully non-conservative // transfer the radiative intershell first from the star that is initially in RLOF (i.e., initiating CE) // note that in the case where both stars are in RLOF (m_RLOFDetails.simultaneousRLOF), star 1 is arbitrarily first to transfer its radiative intershell if (m_Star1->IsRLOF()) { if (utils::Compare(endOfFirstStageMass1 - m_Mass1Final, 0.0) > 0) { - m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass1, -(endOfFirstStageMass1 - m_Mass1Final), endOfFirstStageMass2, m_Star2->IsDegenerate(), 0.0); + m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass1, -(endOfFirstStageMass1 - m_Mass1Final), endOfFirstStageMass2, m_Star2->IsDegenerate(), OPTIONS->CommonEnvelopeSecondStageBeta(), true); } if (utils::Compare(endOfFirstStageMass2 - m_Mass2Final, 0.0) > 0) { - m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass2, -(endOfFirstStageMass2 - m_Mass2Final), m_Mass1Final, m_Star1->IsDegenerate(), 0.0); + m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass2, -(endOfFirstStageMass2 - m_Mass2Final), m_Mass1Final, m_Star1->IsDegenerate(), OPTIONS->CommonEnvelopeSecondStageBeta(), true); } } else if (m_Star2->IsRLOF()) { if (utils::Compare(endOfFirstStageMass2 - m_Mass2Final, 0.0) > 0) { - m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass2, -(endOfFirstStageMass2 - m_Mass2Final), endOfFirstStageMass1, m_Star1->IsDegenerate(), 0.0); + m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass2, -(endOfFirstStageMass2 - m_Mass2Final), endOfFirstStageMass1, m_Star1->IsDegenerate(), OPTIONS->CommonEnvelopeSecondStageBeta(), true); } if (utils::Compare(endOfFirstStageMass1 - m_Mass1Final, 0.0) > 0) { - m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass1, -(endOfFirstStageMass1 - m_Mass1Final), m_Mass2Final, m_Star2->IsDegenerate(), 0.0); + m_SemiMajorAxis = CalculateMassTransferOrbit(endOfFirstStageMass1, -(endOfFirstStageMass1 - m_Mass1Final), m_Mass2Final, m_Star2->IsDegenerate(), OPTIONS->CommonEnvelopeSecondStageBeta(), true); } } } break; @@ -1630,8 +1682,8 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { THROW_ERROR(ERROR::UNKNOWN_CE_FORMALISM); // throw error } - double rRLdfin1 = m_SemiMajorAxis * CalculateRocheLobeRadius_Static(m_Mass1Final, m_Mass2Final); // Roche-lobe radius in AU after CEE, seen by star1 - double rRLdfin2 = m_SemiMajorAxis * CalculateRocheLobeRadius_Static(m_Mass2Final, m_Mass1Final); // Roche-lobe radius in AU after CEE, seen by star2 + double rRLdfin1 = m_SemiMajorAxis * CalculateRocheLobeRadius_Static(m_Mass1Final, m_Mass2Final); // Roche-lobe radius in AU after CEE, seen by star1 + double rRLdfin2 = m_SemiMajorAxis * CalculateRocheLobeRadius_Static(m_Mass2Final, m_Mass1Final); // Roche-lobe radius in AU after CEE, seen by star2 double rRLdfin1Rsol = rRLdfin1 * AU_TO_RSOL; // Roche-lobe radius in Rsol after CEE, seen by star1 double rRLdfin2Rsol = rRLdfin2 * AU_TO_RSOL; // Roche-lobe radius in Rsol after CEE, seen by star2 m_Eccentricity = 0.0; // we assume that a common envelope event (CEE) circularises the binary @@ -1641,13 +1693,13 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { // update stellar type after losing its envelope. Star1, Star2 or both if double CEE. - if (isDonorMS || (!envelopeFlag1 && !envelopeFlag2)) { // stellar merger + if (isDonorMS || (!envelopeFlag1 && !envelopeFlag2) || (m_Star1->IsRLOF() && m_Star1->IsOneOf(WHITE_DWARFS)) || (m_Star2->IsRLOF() && m_Star2->IsOneOf(WHITE_DWARFS))) { // stellar merger m_MassTransferTrackerHistory = MT_TRACKING::MERGER; m_Flags.stellarMerger = true; } else if ((m_Star1->DetermineEnvelopeType()==ENVELOPE::RADIATIVE && !m_Star1->IsOneOf(ALL_MAIN_SEQUENCE)) || (m_Star2->DetermineEnvelopeType()==ENVELOPE::RADIATIVE && !m_Star2->IsOneOf(ALL_MAIN_SEQUENCE)) ) { // check if we have a non-MS radiative-envelope star - if (!OPTIONS->AllowRadiativeEnvelopeStarToSurviveCommonEnvelope() && OPTIONS->CommonEnvelopeFormalism()!=CE_FORMALISM::TWO_STAGE) { // stellar merger + if (!OPTIONS->AllowRadiativeEnvelopeStarToSurviveCommonEnvelope() && OPTIONS->CommonEnvelopeFormalism() != CE_FORMALISM::TWO_STAGE) { // stellar merger m_CEDetails.optimisticCE = true; m_MassTransferTrackerHistory = MT_TRACKING::MERGER; m_Flags.stellarMerger = true; @@ -1659,7 +1711,7 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { m_Flags.stellarMerger = true; } - if (!m_Flags.stellarMerger) { // stellar merger? + if (!m_Flags.stellarMerger) { // stellar merger? // no - continue evolution STELLAR_TYPE stellarType1 = m_Star1->StellarType(); // star 1 stellar type before resolving envelope loss STELLAR_TYPE stellarType2 = m_Star2->StellarType(); // star 2 stellar type before resolving envelope loss @@ -1688,7 +1740,7 @@ void BaseBinaryStar::ResolveCommonEnvelopeEvent() { m_Star1->SetPostCEEValues(); // squirrel away post CEE stellar values for star 1 m_Star2->SetPostCEEValues(); // squirrel away post CEE stellar values for star 2 - SetPostCEEValues(m_SemiMajorAxis * AU_TO_RSOL, m_Eccentricity, rRLdfin1Rsol, rRLdfin2Rsol); // squirrel away post CEE binary values (checks for post-CE RLOF, so should be done at end) + SetPostCEEValues(m_SemiMajorAxis * AU_TO_RSOL, semiMajorAxisAfterStage1 * AU_TO_RSOL, m_Eccentricity, rRLdfin1Rsol, rRLdfin2Rsol); // squirrel away post CEE binary values (checks for post-CE RLOF, so should be done at end) if (m_RLOFDetails.immediateRLOFPostCEE == true && !OPTIONS->AllowImmediateRLOFpostCEToSurviveCommonEnvelope()) {// is there immediate post-CE RLOF which is not allowed? m_MassTransferTrackerHistory = MT_TRACKING::MERGER; @@ -1735,15 +1787,14 @@ void BaseBinaryStar::ResolveMainSequenceMerger() { (utils::Compare(m_Star1->MZAMS(), BRCEK_LOWER_MASS_LIMIT) >= 0) && (utils::Compare(m_Star2->MZAMS(), BRCEK_LOWER_MASS_LIMIT) >= 0)) { - double coreMass1 = m_Star1->MainSequenceCoreMass(); - double coreMass2 = m_Star2->MainSequenceCoreMass(); - - double coreHeliumMass1 = m_Star1->HeliumAbundanceCore() * coreMass1; - double coreHeliumMass2 = m_Star2->HeliumAbundanceCore() * coreMass2; + // total hydrogen masses (from the core, envelope, and the intermediate region between the core and envelope) + double hydrogenMass1 = m_Star1->HydrogenAbundanceCore() * m_Star1->MainSequenceCoreMass() + (m_Star1->HydrogenAbundanceCore() + m_Star1->HydrogenAbundanceSurface()) * (std::min(m_Star1->InitialMainSequenceCoreMass(), m_Star1->Mass()) - m_Star1->MainSequenceCoreMass()) / 2.0 + m_Star1->HydrogenAbundanceSurface() * std::max(m_Star1->Mass() - m_Star1->InitialMainSequenceCoreMass(), 0.0); + double hydrogenMass2 = m_Star2->HydrogenAbundanceCore() * m_Star2->MainSequenceCoreMass() + (m_Star2->HydrogenAbundanceCore() + m_Star2->HydrogenAbundanceSurface()) * (std::min(m_Star2->InitialMainSequenceCoreMass(), m_Star2->Mass()) - m_Star2->MainSequenceCoreMass()) / 2.0 + m_Star2->HydrogenAbundanceSurface() * std::max(m_Star2->Mass() - m_Star2->InitialMainSequenceCoreMass(), 0.0); - finalHydrogenMass = finalMass * initialHydrogenFraction - coreHeliumMass1 - coreHeliumMass2; + // final hydrogen mass in the merger product, assuming that the lost mass comes from the envelope of the star with higher surface hydrogen abundance + finalHydrogenMass = hydrogenMass1 + hydrogenMass2 - (m_Star1->Mass() + m_Star2->Mass() - finalMass) * std::max(m_Star1->HydrogenAbundanceSurface(), m_Star2->HydrogenAbundanceSurface()); } - else finalHydrogenMass = finalMass * initialHydrogenFraction - tau1 * TAMSCoreMass1 * initialHydrogenFraction - tau2 * TAMSCoreMass2 * initialHydrogenFraction; + else finalHydrogenMass = finalMass * initialHydrogenFraction - tau1 * TAMSCoreMass1 * initialHydrogenFraction - tau2 * TAMSCoreMass2 * initialHydrogenFraction; m_SemiMajorAxis = std::numeric_limits::infinity(); // set separation to infinity to avoid subsequent fake interactions with a massless companion (RLOF, CE, etc.) @@ -1775,25 +1826,28 @@ double BaseBinaryStar::CalculateRocheLobeRadius_Static(const double p_MassPrimar /* * Calculate the fraction of specific angular momentum with which the non-accreted mass leaves the system * - * This is gamma (as in Pols's notes) or jloss (as in Belczynski et al. 2008 + * This is gamma (as in Pols's notes) or jloss (as in Belczynski et al. 2008) * which is the fraction of specific angular momentum with which the non-accreted mass leaves the system. - * Macleod_linear comes from Willcox et al. (2022) + * Macleod_linear comes from Willcox et al. (2023) * * Calculation is based on user-specified Angular Momentum Loss prescription * * - * double CalculateGammaAngularMomentumLoss_Static(const double p_DonorMass, const double p_AccretorMass, const bool p_IsAccretorDegenerate) + * double CalculateGammaAngularMomentumLoss_Static(const double p_DonorMass, const double p_AccretorMass, const bool p_IsAccretorDegenerate, const bool p_IsCommonEnvelope) * * @param [IN] p_DonorMass The mass of the donor (Msol) * @param [IN] p_AccretorMass The mass of the accretor (Msol) * @param [IN] p_IsAccretorDegenerate True if the accretor is a degenerate star, false otherwise (need to know up front to keep this function static) + * @param [IN] p_IsCommonEnvelope True if this function is being called while in the (second stage of a 2-stage) CE * @return The fraction of specific angular momentum with which the non-accreted mass leaves the system */ -double BaseBinaryStar::CalculateGammaAngularMomentumLoss_Static(const double p_DonorMass, const double p_AccretorMass, const bool p_IsAccretorDegenerate) { +double BaseBinaryStar::CalculateGammaAngularMomentumLoss_Static(const double p_DonorMass, const double p_AccretorMass, const bool p_IsAccretorDegenerate, const bool p_IsCommonEnvelope) { double gamma; + MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION gammaPrescription = OPTIONS->MassTransferAngularMomentumLossPrescription(); + if (p_IsCommonEnvelope) gammaPrescription = OPTIONS->CommonEnvelopeSecondStageGammaPrescription(); - switch (OPTIONS->MassTransferAngularMomentumLossPrescription()) { // which prescription? + switch (gammaPrescription) { // which prescription? case MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::JEANS : gamma = p_AccretorMass / p_DonorMass; break; // vicinity of the donor @@ -1805,20 +1859,34 @@ double BaseBinaryStar::CalculateGammaAngularMomentumLoss_Static(const double p_D double sumMasses = p_DonorMass + p_AccretorMass; gamma = (M_SQRT2 * sumMasses * sumMasses) / (p_DonorMass * p_AccretorMass); } break; - + case MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::MACLEOD_LINEAR : { // linear interpolation on separation between accretor and L2 point - // interpolate in separation between a_acc and a_L2, both normalized to units of separation a + // Interpolate linearly in separation between a_acc and a_L2, both normalized to units of separation a double q = p_AccretorMass / p_DonorMass; double qPlus1 = 1.0 + q; double aL2 = std::sqrt(M_SQRT2); // roughly, coincides with CIRCUMBINARY_RING def above double aAcc = 1.0 / qPlus1; double fMacleod = p_IsAccretorDegenerate - ? OPTIONS->MassTransferJlossMacLeodLinearFractionDegen() - : OPTIONS->MassTransferJlossMacLeodLinearFractionNonDegen(); + ? OPTIONS->MassTransferJlossLinearFractionDegen() + : OPTIONS->MassTransferJlossLinearFractionNonDegen(); double aGamma = aAcc + (aL2 - aAcc) * fMacleod; gamma = aGamma * aGamma * qPlus1 * qPlus1 / q; } break; + case MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::KLENCKI_LINEAR : { // linear interpolation on separation between accretor and L2 point + // Interpolate linearly in specific AM loss parameter gamma + double q = p_AccretorMass / p_DonorMass; + double qPlus1 = 1.0 + q; + double qPlus1SquaredByQ = qPlus1 * qPlus1 / q; + double aL2 = std::sqrt(M_SQRT2); // roughly, coincides with CIRCUMBINARY_RING def above + double aAcc = 1.0 / qPlus1; + double gammaL2 = aL2 * aL2 * qPlus1SquaredByQ; + double gammaAcc = aAcc * aAcc * qPlus1SquaredByQ; + double fKlencki = p_IsAccretorDegenerate + ? OPTIONS->MassTransferJlossLinearFractionDegen() + : OPTIONS->MassTransferJlossLinearFractionNonDegen(); + gamma = gammaAcc + (gammaL2 - gammaAcc) * fKlencki; + } break; default: // unknown prescription // the only way this can happen is if someone added an MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION // and it isn't accounted for in this code. We should not default here, with or without a warning. @@ -1843,20 +1911,23 @@ double BaseBinaryStar::CalculateGammaAngularMomentumLoss_Static(const double p_D * const double p_DeltaMassDonor, * const double p_AccretorMass, const bool p_IsAccretorDegenerate, - * const double p_FractionAccreted) + * const double p_FractionAccreted + * const bool p_IsCommonEnvelope) * * @param [IN] p_DonorMass Donor mass * @param [IN] p_DeltaMassDonor Change in donor mass * @param [IN] p_AccretorMass Accretor mass * @param [IN] p_IsAccretorDegenerate Flag for degenerate accretors * @param [IN] p_FractionAccreted Mass fraction lost from donor accreted by accretor + * @param [IN] p_IsCommonEnvelope True if this function is being called while in the (second stage of a 2-stage) CE * @return Semi-major axis */ double BaseBinaryStar::CalculateMassTransferOrbit(const double p_DonorMass, const double p_DeltaMassDonor, const double p_AccretorMass, const bool p_IsAccretorDegenerate, - const double p_FractionAccreted) { + const double p_FractionAccreted, + const bool p_IsCommonEnvelope) { double semiMajorAxis = m_SemiMajorAxis; @@ -1868,8 +1939,8 @@ double BaseBinaryStar::CalculateMassTransferOrbit(const double p // Use boost adaptive ODE solver for speed and accuracy struct ode { - double p_MassDonor0, p_MassAccretor0, p_FractionAccreted, p_IsAccretorDegenerate; - ode(double massDonor0, double massAccretor0, double fractionAccreted, bool isAccretorDegenerate) : p_MassDonor0(massDonor0), p_MassAccretor0(massAccretor0), p_FractionAccreted(fractionAccreted), p_IsAccretorDegenerate(isAccretorDegenerate) {} + double p_MassDonor0, p_MassAccretor0, p_FractionAccreted, p_IsAccretorDegenerate, p_IsCommonEnvelope; + ode(double massDonor0, double massAccretor0, double fractionAccreted, bool isAccretorDegenerate, bool isCommonEnvelope) : p_MassDonor0(massDonor0), p_MassAccretor0(massAccretor0), p_FractionAccreted(fractionAccreted), p_IsAccretorDegenerate(isAccretorDegenerate), p_IsCommonEnvelope(isCommonEnvelope) {} // x is the current state of the ODE (x[0] = semi-major axis a) // dxdm is the change of state wrt mass (dxdm[0] = dadm) @@ -1877,12 +1948,12 @@ double BaseBinaryStar::CalculateMassTransferOrbit(const double p void operator()(state_type const& x, state_type& dxdm, double p_MassChange ) const { double massD = p_MassDonor0 + p_MassChange; double massA = p_MassAccretor0 - p_MassChange * p_FractionAccreted; - double jLoss = CalculateGammaAngularMomentumLoss_Static(massD, massA, p_IsAccretorDegenerate); + double jLoss = CalculateGammaAngularMomentumLoss_Static(massD, massA, p_IsAccretorDegenerate, p_IsCommonEnvelope); dxdm[0] = (-2.0 / massD) * (1.0 - (p_FractionAccreted * (massD / massA)) - ((1.0 - p_FractionAccreted) * (jLoss + 0.5) * (massD / (massA + massD)))) * x[0]; } }; - integrate_adaptive(controlled_stepper, ode{ p_DonorMass, p_AccretorMass, p_FractionAccreted, p_IsAccretorDegenerate }, x, 0.0, p_DeltaMassDonor, p_DeltaMassDonor / 1000.0); + integrate_adaptive(controlled_stepper, ode{ p_DonorMass, p_AccretorMass, p_FractionAccreted, p_IsAccretorDegenerate, p_IsCommonEnvelope }, x, 0.0, p_DeltaMassDonor, p_DeltaMassDonor / 1000.0); semiMajorAxis = x[0]; } @@ -1942,8 +2013,8 @@ void BaseBinaryStar::CalculateWindsMassLoss(double p_Dt) { m_Star2->HaltWinds(); } else { - if (OPTIONS->UseMassLoss()) { // mass loss enabled? - + if (OPTIONS->MassLossPrescription() != MASS_LOSS_PRESCRIPTION::ZERO) { // mass loss enabled? + // yes double mWinds1 = m_Star1->CalculateMassLossValues(p_Dt, true); // calculate new values assuming mass loss applied double mWinds2 = m_Star2->CalculateMassLossValues(p_Dt, true); // calculate new values assuming mass loss applied @@ -1954,6 +2025,10 @@ void BaseBinaryStar::CalculateWindsMassLoss(double p_Dt) { m_aMassLossDiff = aWinds - m_SemiMajorAxisPrev; // change to orbit (semi-major axis) due to winds mass loss } + else { // reset total mass loss rate to zero + m_Star1->UpdateTotalMassLossRate(0.0); + m_Star2->UpdateTotalMassLossRate(0.0); + } } } @@ -2008,28 +2083,25 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { jLoss = CalculateGammaAngularMomentumLoss(); // no - re-calculate angular momentum } - m_MassTransferTimescale = MASS_TRANSFER_TIMESCALE::NONE; // initial reset - double betaThermal = 0.0; // fraction of mass accreted if accretion proceeds on thermal timescale - double maximumAccretionRate = 0.0; // accretion rate if accretion proceeds on thermal timescale + m_MassTransferTimescale = MT_TIMESCALE::NONE; // initial reset + double donorMassLossRateThermal = m_Donor->CalculateThermalMassLossRate(); - std::tie(maximumAccretionRate, betaThermal) = m_Accretor->CalculateMassAcceptanceRate(donorMassLossRateThermal, - m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius), - donorIsHeRich); - double massDiffDonor = 0.0; - + std::tie(std::ignore, m_FractionAccreted) = m_Accretor->CalculateMassAcceptanceRate(donorMassLossRateThermal, + m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius), donorIsHeRich); + double massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, m_FractionAccreted, 0.0); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, fixed beta + double betaThermal = m_FractionAccreted; // may need these later if the mass transfer proceeds on a thermal timescale, so we store them to avoid recomputing + double massDiffDonorThermal = massDiffDonor; + // can the mass transfer happen on a nuclear timescale? if (m_Donor->IsOneOf(NON_COMPACT_OBJECTS)) { - // technically, we do not know how much mass the accretor should gain until we do the calculation, - // which impacts the RL size, so we will check whether a nuclear timescale MT was feasible later - double maximumAccretedMass = maximumAccretionRate * m_Dt; - if (OPTIONS->MassTransferAccretionEfficiencyPrescription() == MT_ACCRETION_EFFICIENCY_PRESCRIPTION::THERMALLY_LIMITED) { - massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, -1.0, maximumAccretedMass); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, fixed accretion amount - m_FractionAccreted = std::min(maximumAccretedMass, massDiffDonor) / massDiffDonor; - } - else { - m_FractionAccreted = maximumAccretionRate / donorMassLossRateThermal; // relevant for MT_ACCRETION_EFFICIENCY_PRESCRIPTION::FIXED_FRACTION - massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, m_FractionAccreted, 0.0); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, fixed beta + // if MT_ACCRETION_EFFICIENCY_PRESCRIPTION::FIXED_FRACTION, then CalculateMassAcceptanceRate() already computed the correct m_FractionAccreted and massDiffDonor (no difference between nuclear and thermal timescale MT) + if (OPTIONS->MassTransferAccretionEfficiencyPrescription() == MT_ACCRETION_EFFICIENCY_PRESCRIPTION::THERMALLY_LIMITED || OPTIONS->MassTransferAccretionEfficiencyPrescription() == MT_ACCRETION_EFFICIENCY_PRESCRIPTION::HAMSTARS) { + // technically, we do not know how much mass the accretor should gain until we do the calculation, + // which impacts the RL size, so we will check whether a nuclear timescale MT was feasible later + massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, -1.0, m_Dt); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe, estimating accretion efficiency based on a mass donation rate of massDiffDonor/m_Dt for self-consistency + std::tie(std::ignore, m_FractionAccreted) = m_Accretor->CalculateMassAcceptanceRate(massDiffDonor/m_Dt, + m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius), donorIsHeRich); } // check that the star really would have consistently fit into the Roche lobe @@ -2037,18 +2109,18 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { double zetaLobe = CalculateZetaRocheLobe(jLoss, m_FractionAccreted); if (utils::Compare(zetaEquilibrium, zetaLobe) > 0 && massDiffDonor > 0.0) { // yes, it's nuclear timescale mass transfer; no need for utils::Compare here m_MassLossRateInRLOF = massDiffDonor / m_Dt; - m_MassTransferTimescale = MASS_TRANSFER_TIMESCALE::NUCLEAR; + m_MassTransferTimescale = MT_TIMESCALE::NUCLEAR; m_ZetaStar = zetaEquilibrium; m_ZetaLobe = zetaLobe; } } - if (m_MassTransferTimescale != MASS_TRANSFER_TIMESCALE::NUCLEAR) { // thermal timescale mass transfer (we will check for dynamically unstable / CE mass transfer later) - m_ZetaLobe = CalculateZetaRocheLobe(jLoss, betaThermal); + if (m_MassTransferTimescale != MT_TIMESCALE::NUCLEAR) { // thermal timescale mass transfer (we will check for dynamically unstable / CE mass transfer later) + m_FractionAccreted = betaThermal; // m_FractionAccreted and massDiffDonor already computed for the thermal mass transfer case + massDiffDonor = massDiffDonorThermal; + m_ZetaLobe = CalculateZetaRocheLobe(jLoss, m_FractionAccreted); m_ZetaStar = m_Donor->CalculateZetaAdiabatic(); m_MassLossRateInRLOF = donorMassLossRateThermal; - m_FractionAccreted = betaThermal; - m_MassTransferTimescale = MASS_TRANSFER_TIMESCALE::THERMAL; - massDiffDonor = MassLossToFitInsideRocheLobe(this, m_Donor, m_Accretor, betaThermal, 0.0); // use root solver to determine how much mass should be lost from the donor to allow it to fit within the Roche lobe + m_MassTransferTimescale = MT_TIMESCALE::THERMAL; } double aInitial = m_SemiMajorAxis; // semi-major axis in default units, AU, current timestep @@ -2057,16 +2129,16 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { // Calculate conditions for automatic (in)stability for case BB bool caseBBAlwaysStable = OPTIONS->CaseBBStabilityPrescription() == CASE_BB_STABILITY_PRESCRIPTION::ALWAYS_STABLE; bool caseBBAlwaysUnstable = OPTIONS->CaseBBStabilityPrescription() == CASE_BB_STABILITY_PRESCRIPTION::ALWAYS_UNSTABLE; - bool caseBBAlwaysUnstableOntoNSBH = OPTIONS->CaseBBStabilityPrescription() == CASE_BB_STABILITY_PRESCRIPTION::ALWAYS_STABLE_ONTO_NSBH; + bool caseBBAlwaysStableOntoNSBH = OPTIONS->CaseBBStabilityPrescription() == CASE_BB_STABILITY_PRESCRIPTION::ALWAYS_STABLE_ONTO_NSBH; bool donorIsHeHGorHeGB = m_Donor->IsOneOf({ STELLAR_TYPE::NAKED_HELIUM_STAR_HERTZSPRUNG_GAP, STELLAR_TYPE::NAKED_HELIUM_STAR_GIANT_BRANCH }); bool accretorIsNSorBH = m_Accretor->IsOneOf({ STELLAR_TYPE::NEUTRON_STAR, STELLAR_TYPE::BLACK_HOLE }); bool accretorIsWD = m_Accretor->IsOneOf(WHITE_DWARFS); // Determine stability bool isUnstable = false; - if (donorIsHeHGorHeGB && (caseBBAlwaysStable || caseBBAlwaysUnstable || (caseBBAlwaysUnstableOntoNSBH && accretorIsNSorBH))) { // determine stability based on case BB - isUnstable = (caseBBAlwaysUnstable || (caseBBAlwaysUnstableOntoNSBH && accretorIsNSorBH)); // already established that donor is HeHG or HeGB - need to check if new case BB prescriptions are added - } + if (donorIsHeHGorHeGB && (caseBBAlwaysStable || caseBBAlwaysUnstable || (caseBBAlwaysStableOntoNSBH && !accretorIsNSorBH))) { // determine stability based on case BB + isUnstable = (caseBBAlwaysUnstable || (caseBBAlwaysStableOntoNSBH && !accretorIsNSorBH)); // already established that donor is HeHG or HeGB - need to check if new case BB prescriptions are added + } else if (accretorIsWD && (m_Accretor->WhiteDwarfAccretionRegime() == ACCRETION_REGIME::HELIUM_WHITE_DWARF_HYDROGEN_ACCUMULATION)) { isUnstable = true; if (!m_Donor->IsOneOf(GIANTS)) m_Flags.stellarMerger = true; @@ -2090,7 +2162,7 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { bool isEnvelopeRemoved = false; if (utils::Compare(m_Donor->CoreMass(), 0.0) > 0 && utils::Compare(envMassDonor, 0.0) > 0) { // donor has a core and an envelope - if (m_MassTransferTimescale == MASS_TRANSFER_TIMESCALE::THERMAL || utils::Compare (massDiffDonor, envMassDonor) >= 0) { + if (m_MassTransferTimescale == MT_TIMESCALE::THERMAL || utils::Compare (massDiffDonor, envMassDonor) >= 0) { // remove entire envelope if thermal timescale MT from a giant or if the amount of necessary mass loss exceeds the envelope mass massDiffDonor = -envMassDonor; isEnvelopeRemoved = true; @@ -2124,7 +2196,7 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { m_Donor->SetMassTransferDiffAndResolveWDShellChange(massDiffDonor); // set new mass of donor m_Accretor->SetMassTransferDiffAndResolveWDShellChange(massGainAccretor); // set new mass of accretor - aFinal = CalculateMassTransferOrbit(m_Donor->Mass(), massDiffDonor, *m_Accretor, m_FractionAccreted); // calculate new orbit + aFinal = CalculateMassTransferOrbit(m_Donor->Mass(), massDiffDonor, *m_Accretor, m_FractionAccreted, false); // calculate new orbit m_aMassTransferDiff = aFinal - aInitial; // set change in orbit (semi-major axis) STELLAR_TYPE stellarTypeDonor = m_Donor->StellarType(); // donor stellar type before resolving envelope loss @@ -2146,12 +2218,14 @@ void BaseBinaryStar::CalculateMassTransfer(const double p_Dt) { } } - // Check for recycled pulsars. Not considering CEE as a way of recycling NSs. - if (!m_CEDetails.CEEnow && m_Accretor->IsOneOf({ STELLAR_TYPE::NEUTRON_STAR })) { // accretor is a neutron star - m_Donor->SetRLOFOntoNS(); // donor donated mass to a neutron star + // Check for recycled pulsars. Not considering CEE as a way of recycling NSs. + if (!m_CEDetails.CEEnow && m_Accretor->IsOneOf({ STELLAR_TYPE::NEUTRON_STAR })) { // accretor is a neutron star, system is not in CE m_Accretor->SetRecycledNS(); // accretor is (was) a recycled NS - } - + } + else if (m_CEDetails.CEEnow && m_Accretor->IsOneOf({ STELLAR_TYPE::NEUTRON_STAR }) + && OPTIONS->NeutronStarAccretionInCE() != NS_ACCRETION_IN_CE::ZERO) { // accretor is a neutron star, system is in CE + m_Accretor->SetRecycledNS(); // accretor is (was) a recycled NS + } } @@ -2270,7 +2344,7 @@ void BaseBinaryStar::InitialiseMassTransfer() { m_MassTransferTrackerHistory = MT_TRACKING::NO_MASS_TRANSFER; // Initiating flag, every timestep, to NO_MASS_TRANSFER. If it undergoes to MT or CEE, it should change. - m_MassTransferTimescale = MASS_TRANSFER_TIMESCALE::NONE; + m_MassTransferTimescale = MT_TIMESCALE::NONE; m_MassLossRateInRLOF = 0.0; m_Star1->InitialiseMassTransfer(m_CEDetails.CEEnow, m_SemiMajorAxis, m_Eccentricity); // initialise mass transfer for star1 @@ -2561,7 +2635,7 @@ void BaseBinaryStar::ResolveMassChanges() { * Adjust stellar rotation and determine how much excess angular momentum is deposited into the orbit (or removed from it to spin up the accretor) * May limit the amount of accreted mass depending on the ResponseToSpinUp(): * KEPLERIAN_LIMIT forces mass transfer to become non-conservative once star (approximately) reaches super-critical rotation - * Under TRANSFER_TO_ORBIT, the star continues to accrete, but excess angular momentum is deposited in the orbit + * Under TRANSFER_TO_ORBIT, the star continues to accrete, but excess angular momentum is deposited in the orbit * NO_LIMIT allows arbitrary super-critical accretion, to match legacy choices * * double ResolveAccretionAngularMomentumGain(BinaryConstituentStar *p_Accretor, BinaryConstituentStar *p_Donor, double p_MassChange) @@ -2688,35 +2762,44 @@ void BaseBinaryStar::ProcessTides(const double p_Dt) { } break; - case TIDES_PRESCRIPTION::KAPIL2024: { // KAPIL2024 + case TIDES_PRESCRIPTION::KAPIL2025: { // KAPIL2025 - // Evolve binary semi-major axis, eccentricity, and spin of each star based on Kapil et al., 2024 + // Evolve binary semi-major axis, eccentricity, and spin of each star based on Kapil et al., 2025 - DBL_DBL_DBL_DBL ImKlm1 = m_Star1->CalculateImKlmTidal(omega, m_SemiMajorAxis, m_Star2->Mass()); - DBL_DBL_DBL_DBL ImKlm2 = m_Star2->CalculateImKlmTidal(omega, m_SemiMajorAxis, m_Star1->Mass()); + DBL_DBL_DBL_DBL ImKnm1_tidal = m_Star1->CalculateImKnmTidal(omega, m_SemiMajorAxis, m_Star2->Mass()); + DBL_DBL_DBL_DBL ImKnm2_tidal = m_Star2->CalculateImKnmTidal(omega, m_SemiMajorAxis, m_Star1->Mass()); - double DSemiMajorAxis1Dt = CalculateDSemiMajorAxisTidalDt(ImKlm1, m_Star1); // change in semi-major axis from star1 - double DSemiMajorAxis2Dt = CalculateDSemiMajorAxisTidalDt(ImKlm2, m_Star2); // change in semi-major axis from star2 + double DSemiMajorAxis1Dt_tidal = CalculateDSemiMajorAxisTidalDt(ImKnm1_tidal, m_Star1); // change in semi-major axis from star1 + double DSemiMajorAxis2Dt_tidal = CalculateDSemiMajorAxisTidalDt(ImKnm2_tidal, m_Star2); // change in semi-major axis from star2 - double DEccentricity1Dt = CalculateDEccentricityTidalDt(ImKlm1, m_Star1); // change in eccentricity from star1 - double DEccentricity2Dt = CalculateDEccentricityTidalDt(ImKlm2, m_Star2); // change in eccentricity from star2 + double DEccentricity1Dt_tidal = CalculateDEccentricityTidalDt(ImKnm1_tidal, m_Star1); // change in eccentricity from star1 + double DEccentricity2Dt_tidal = CalculateDEccentricityTidalDt(ImKnm2_tidal, m_Star2); // change in eccentricity from star2 - double DOmega1Dt = CalculateDOmegaTidalDt(ImKlm1, m_Star1); // change in spin from star1 - double DOmega2Dt = CalculateDOmegaTidalDt(ImKlm2, m_Star2); // change in spin from star2 + double DOmega1Dt_tidal = CalculateDOmegaTidalDt(ImKnm1_tidal, m_Star1); // change in spin from star1 + double DOmega2Dt_tidal = CalculateDOmegaTidalDt(ImKnm2_tidal, m_Star2); // change in spin from star2 // limit change in stellar and orbital properties from tides to a maximum fraction of the current value double fraction_tidal_change = 1.0; - fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * OrbitalAngularVelocity() / (DOmega1Dt * p_Dt * MYR_TO_YEAR))); - fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * OrbitalAngularVelocity() / (DOmega2Dt * p_Dt * MYR_TO_YEAR))); - fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_SemiMajorAxis / ((DSemiMajorAxis1Dt + DSemiMajorAxis2Dt) * p_Dt * MYR_TO_YEAR))); - fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_Eccentricity / ((DEccentricity1Dt + DEccentricity2Dt) * p_Dt * MYR_TO_YEAR))); + fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * OrbitalAngularVelocity() / (DOmega1Dt_tidal * p_Dt * MYR_TO_YEAR))); + fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * OrbitalAngularVelocity() / (DOmega2Dt_tidal * p_Dt * MYR_TO_YEAR))); + fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_SemiMajorAxis / ((DSemiMajorAxis1Dt_tidal + DSemiMajorAxis2Dt_tidal) * p_Dt * MYR_TO_YEAR))); + fraction_tidal_change = std::min(fraction_tidal_change, std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_Eccentricity / ((DEccentricity1Dt_tidal + DEccentricity2Dt_tidal) * p_Dt * MYR_TO_YEAR))); - m_Star1->SetOmega(m_Star1->Omega() + fraction_tidal_change * (DOmega1Dt * p_Dt * MYR_TO_YEAR)); // evolve star 1 spin - m_Star2->SetOmega(m_Star2->Omega() + fraction_tidal_change * (DOmega2Dt * p_Dt * MYR_TO_YEAR)); // evolve star 2 spin - m_SemiMajorAxis = m_SemiMajorAxis + fraction_tidal_change * ((DSemiMajorAxis1Dt + DSemiMajorAxis2Dt) * p_Dt * MYR_TO_YEAR); // evolve separation - m_Eccentricity = m_Eccentricity + fraction_tidal_change * ((DEccentricity1Dt + DEccentricity2Dt) * p_Dt * MYR_TO_YEAR); // evolve eccentricity + m_Star1->SetOmega(m_Star1->Omega() + fraction_tidal_change * (DOmega1Dt_tidal * p_Dt * MYR_TO_YEAR)); // evolve star 1 spin + m_Star2->SetOmega(m_Star2->Omega() + fraction_tidal_change * (DOmega2Dt_tidal * p_Dt * MYR_TO_YEAR)); // evolve star 2 spin + m_SemiMajorAxis = m_SemiMajorAxis + fraction_tidal_change * ((DSemiMajorAxis1Dt_tidal + DSemiMajorAxis2Dt_tidal) * p_Dt * MYR_TO_YEAR); // evolve separation + m_Eccentricity = m_Eccentricity + fraction_tidal_change * ((DEccentricity1Dt_tidal + DEccentricity2Dt_tidal) * p_Dt * MYR_TO_YEAR); // evolve eccentricity + + m_CircularizationTimescale = - m_Eccentricity / (DEccentricity1Dt_tidal + DEccentricity2Dt_tidal) * YEAR_TO_MYR; // Circularization timescale in Myr (for output files) + m_CircularizationTimescale = (std::isnan(m_CircularizationTimescale) || std::isinf(m_CircularizationTimescale))? 0.0 : m_CircularizationTimescale; // check for NaN or Inf for circular binaries - m_TotalAngularMomentum = CalculateAngularMomentum(); // re-calculate angular momenta + m_SynchronizationTimescale1 = - (m_Star1->Omega() - omega) / DOmega1Dt_tidal * YEAR_TO_MYR; // Synchronization timescale for Star1 in Myr (for output files) + m_SynchronizationTimescale1 = (std::isnan(m_SynchronizationTimescale1) || std::isinf(m_SynchronizationTimescale1))? 0.0 : m_SynchronizationTimescale1; // check for NaN or Inf for synchronized binaries + + m_SynchronizationTimescale2 = - (m_Star2->Omega() - omega) / DOmega2Dt_tidal * YEAR_TO_MYR; // Synchronization timescale for Star2 in Myr (for output files) + m_SynchronizationTimescale2 = (std::isnan(m_SynchronizationTimescale2) || std::isinf(m_SynchronizationTimescale2))? 0.0 : m_SynchronizationTimescale2; // check for NaN or Inf for synchronized binaries + + m_TotalAngularMomentum = CalculateAngularMomentum(); // re-calculate angular momenta m_OrbitalAngularMomentum = CalculateOrbitalAngularMomentum(m_Star1->Mass(), m_Star2->Mass(), m_SemiMajorAxis, m_Eccentricity); } break; @@ -2953,12 +3036,15 @@ void BaseBinaryStar::EmitGravitationalWave(const double p_Dt) { * * double ChooseTimestep(const double p_Multiplier) * - * @param [IN] p_Multiplier timestep multiplier + * @param [IN] p_Factor factor applied to timestep (in addition to multipliers) * @return new timestep in Myr */ -double BaseBinaryStar::ChooseTimestep(const double p_Multiplier) { +double BaseBinaryStar::ChooseTimestep(const double p_Factor) { + + double dt1 = m_Star1->CalculateTimestep() * OPTIONS->TimestepMultipliers(static_cast(m_Star1->StellarType())); + double dt2 = m_Star2->CalculateTimestep() * OPTIONS->TimestepMultipliers(static_cast(m_Star2->StellarType())); - double dt = std::min(m_Star1->CalculateTimestep(), m_Star2->CalculateTimestep()); // dt = smaller of timesteps required by individual stars + double dt = std::min(dt1, dt2); // dt = smaller of timesteps required by individual stars if (!IsUnbound()) { // check that binary is bound @@ -2968,35 +3054,41 @@ double BaseBinaryStar::ChooseTimestep(const double p_Multiplier) { if ((utils::Compare(radiusToRL1 * (1.0 + 2.0 * OPTIONS->RadialChangeFraction()), 1.0) >= 0 && utils::Compare(radiusToRL1 * (1.0 + 0.5 * OPTIONS->RadialChangeFraction()), 1.0) <= 0) || (utils::Compare(radiusToRL2 * (1.0 + 2.0 * OPTIONS->RadialChangeFraction()), 1.0) >= 0 && utils::Compare(radiusToRL2 * (1.0 + 0.5 * OPTIONS->RadialChangeFraction()), 1.0) <= 0)) dt /= 2.0; - + + // limit time step for stars losing mass on nuclear timescale + if (utils::Compare(radiusToRL1 * (1.0 + 0.5 * OPTIONS->RadialChangeFraction()), 1.0) > 0) + dt = std::min(dt, 0.5 * OPTIONS->RadialChangeFraction() * m_Star1->CalculateRadialExpansionTimescaleDuringMassTransfer()); + if (utils::Compare(radiusToRL2 * (1.0 + 0.5 * OPTIONS->RadialChangeFraction()), 1.0) > 0) + dt = std::min(dt, 0.5 * OPTIONS->RadialChangeFraction() * m_Star2->CalculateRadialExpansionTimescaleDuringMassTransfer()); + if (OPTIONS->EmitGravitationalRadiation()) { // emitting GWs? dt = std::min(dt, -1.0E-2 * m_SemiMajorAxis / m_DaDtGW); // yes - reduce timestep if necessary to ensure that the orbital separation does not change by more than ~1% per timestep due to GW emission } - if (OPTIONS->TidesPrescription() == TIDES_PRESCRIPTION::KAPIL2024) { // tides prescription = KAPIL2024 + if (OPTIONS->TidesPrescription() == TIDES_PRESCRIPTION::KAPIL2025) { // tides prescription = KAPIL2025 // yes - need to adjust dt double omega = OrbitalAngularVelocity(); - DBL_DBL_DBL_DBL ImKlm1 = m_Star1->CalculateImKlmTidal(omega, m_SemiMajorAxis, m_Star2->Mass()); - DBL_DBL_DBL_DBL ImKlm2 = m_Star2->CalculateImKlmTidal(omega, m_SemiMajorAxis, m_Star1->Mass()); + DBL_DBL_DBL_DBL ImKnm1_tidal = m_Star1->CalculateImKnmTidal(omega, m_SemiMajorAxis, m_Star2->Mass()); + DBL_DBL_DBL_DBL ImKnm2_tidal = m_Star2->CalculateImKnmTidal(omega, m_SemiMajorAxis, m_Star1->Mass()); - double DSemiMajorAxis1DtTidal = CalculateDSemiMajorAxisTidalDt(ImKlm1, m_Star1); - double DSemiMajorAxis2DtTidal = CalculateDSemiMajorAxisTidalDt(ImKlm2, m_Star2); + double DSemiMajorAxis1Dt_tidal = CalculateDSemiMajorAxisTidalDt(ImKnm1_tidal, m_Star1); + double DSemiMajorAxis2Dt_tidal = CalculateDSemiMajorAxisTidalDt(ImKnm2_tidal, m_Star2); - double DEccentricity1DtTidal = CalculateDEccentricityTidalDt(ImKlm1, m_Star1); - double DEccentricity2DtTidal = CalculateDEccentricityTidalDt(ImKlm2, m_Star2); + double DEccentricity1Dt_tidal = CalculateDEccentricityTidalDt(ImKnm1_tidal, m_Star1); + double DEccentricity2Dt_tidal = CalculateDEccentricityTidalDt(ImKnm2_tidal, m_Star2); - double DOmega1Dt_tidal = CalculateDOmegaTidalDt(ImKlm1, m_Star1); - double DOmega2Dt_tidal = CalculateDOmegaTidalDt(ImKlm2, m_Star2); + double DOmega1Dt_tidal = CalculateDOmegaTidalDt(ImKnm1_tidal, m_Star1); + double DOmega2Dt_tidal = CalculateDOmegaTidalDt(ImKnm2_tidal, m_Star2); // Ensure that the change in orbital and spin properties due to tides in a single timestep is constrained (to 1 percent by default) // Limit the spin evolution of each star based on the orbital frequency rather than its spin frequency, since tides should not cause major problems until synchronization. - double Dt_SemiMajorAxis1Tidal = utils::Compare(DSemiMajorAxis1DtTidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_SemiMajorAxis / DSemiMajorAxis1DtTidal) * YEAR_TO_MYR; - double Dt_SemiMajorAxis2Tidal = utils::Compare(DSemiMajorAxis2DtTidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_SemiMajorAxis / DSemiMajorAxis2DtTidal) * YEAR_TO_MYR; + double Dt_SemiMajorAxis1Tidal = utils::Compare(DSemiMajorAxis1Dt_tidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_SemiMajorAxis / DSemiMajorAxis1Dt_tidal) * YEAR_TO_MYR; + double Dt_SemiMajorAxis2Tidal = utils::Compare(DSemiMajorAxis2Dt_tidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_SemiMajorAxis / DSemiMajorAxis2Dt_tidal) * YEAR_TO_MYR; double Dt_SemiMajorAxisTidal = std::min(Dt_SemiMajorAxis1Tidal, Dt_SemiMajorAxis2Tidal); - double Dt_Eccentricity1Tidal = utils::Compare(DEccentricity1DtTidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_Eccentricity / DEccentricity1DtTidal) * YEAR_TO_MYR; - double Dt_Eccentricity2Tidal = utils::Compare(DEccentricity2DtTidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_Eccentricity / DEccentricity2DtTidal) * YEAR_TO_MYR; + double Dt_Eccentricity1Tidal = utils::Compare(DEccentricity1Dt_tidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_Eccentricity / DEccentricity1Dt_tidal) * YEAR_TO_MYR; + double Dt_Eccentricity2Tidal = utils::Compare(DEccentricity2Dt_tidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * m_Eccentricity / DEccentricity2Dt_tidal) * YEAR_TO_MYR; double Dt_EccentricityTidal = std::min(Dt_Eccentricity1Tidal, Dt_Eccentricity2Tidal); double Dt_Omega1Tidal = utils::Compare(DOmega1Dt_tidal, 0.0) == 0 ? dt : std::abs(TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC * omega / DOmega1Dt_tidal) * YEAR_TO_MYR; @@ -3007,7 +3099,7 @@ double BaseBinaryStar::ChooseTimestep(const double p_Multiplier) { } } - dt *= p_Multiplier; + dt *= OPTIONS->TimestepMultiplier() * p_Factor; return std::max(std::round(dt / TIMESTEP_QUANTUM) * TIMESTEP_QUANTUM, TIDES_MINIMUM_FRACTIONAL_NUCLEAR_TIME * NUCLEAR_MINIMUM_TIMESTEP); // quantised and not less than minimum } @@ -3079,7 +3171,7 @@ void BaseBinaryStar::EvaluateBinary(const double p_Dt) { } CalculateEnergyAndAngularMomentum(); // perform energy and angular momentum calculations - + ProcessTides(p_Dt); // process tides if required // assign new values to "previous" values, for following timestep @@ -3240,7 +3332,7 @@ EVOLUTION_STATUS BaseBinaryStar::Evolve() { } // we want the first timestep to be small - calculate timestep and divide by 1000.0 - dt = ChooseTimestep(OPTIONS->TimestepMultiplier() / 1000.0); // calculate timestep - make first step small + dt = ChooseTimestep(0.001); // calculate timestep - make first step small } unsigned long int stepNum = 1; @@ -3341,6 +3433,46 @@ EVOLUTION_STATUS BaseBinaryStar::Evolve() { (void)PrintDetailedOutput(m_Id, BSE_DETAILED_RECORD_TYPE::TIMESTEP_COMPLETED); // print (log) detailed output: this is after all changes made in the timestep + // check thresholds for system snapshot printing + // don't use utils::Compare() here - not for time/age + + bool printSysSnapshotRec = false; // so we only print this timestep once + + // age threshold + // we print a record each timestep that either star crosses the threshold from below + // notes: + // (a) the age of individual stars can drop for various reasons (phase change, rejuvenation, winds/mass transfer, etc.), + // and if the age of a star drops below an age threshold, we will log another record if that star then ages beyond + // the same threshold (so we might log several records for the same star crossing the same threshold if the age of + // the star oscillates around the threshold) + // (b) we will print multiple records for exceeding the age threshold if the constituent stars exceed the age threshold + // at different timesteps (likely) + for (size_t threshold = 0; threshold < OPTIONS->SystemSnapshotAgeThresholds().size(); threshold++) { // for each system snapshot age threshold + + double thresholdValue = OPTIONS->SystemSnapshotAgeThresholds(threshold); // this threshold value + + // flag need to print (log) system snapshot record + // we don't want to print multiple records for the same timestep, so we flag need rather than print here + printSysSnapshotRec |= m_SystemSnapshotAgeFlags1[threshold] < 0.0 && m_Star1->Age() >= thresholdValue; // star1 + printSysSnapshotRec |= m_SystemSnapshotAgeFlags2[threshold] < 0.0 && m_Star2->Age() >= thresholdValue; // star2 + + // record the current age of the stars in the threshold flag - this is how we check for re-crossing a threshold + // if the age of a star has dropped below the threshold value, we reset the threshold flag for that star + // the check will fail if the star hasn't crossed the threshold already, but the flag will be -1.0 anyway + m_SystemSnapshotAgeFlags1[threshold] = (m_Star1->Age() < thresholdValue) ? -1.0 : m_Star1->Age(); + m_SystemSnapshotAgeFlags2[threshold] = (m_Star2->Age() < thresholdValue) ? -1.0 : m_Star2->Age(); + } + + // time threshold + // we print a record at the first timestep that the simulation time exceeds the time threshold + for (size_t threshold = 0; threshold < OPTIONS->SystemSnapshotTimeThresholds().size(); threshold++) { // for each system snapshot time threshold + if (!m_SystemSnapshotTimeFlags[threshold] && Time() >= OPTIONS->SystemSnapshotTimeThresholds(threshold)) { // need to action? + m_SystemSnapshotTimeFlags[threshold] = true; // yes, flag action taken + printSysSnapshotRec = true; // flag need to print (log) system snapshot record + } + } + + if (printSysSnapshotRec) (void)PrintSystemSnapshotLog(); // print (log) system snapshot record if necessary if (evolutionStatus == EVOLUTION_STATUS::CONTINUE) { // continue evolution? // yes @@ -3370,7 +3502,7 @@ EVOLUTION_STATUS BaseBinaryStar::Evolve() { dt = timesteps[stepNum]; } else { // no - not using user-provided timesteps - dt = ChooseTimestep(OPTIONS->TimestepMultiplier()); + dt = ChooseTimestep(); } stepNum++; // increment stepNum @@ -3426,7 +3558,7 @@ EVOLUTION_STATUS BaseBinaryStar::Evolve() { m_EvolutionStatus = evolutionStatus; - (void)PrintBinarySystemParameters(); // print (log) binary system parameters + (void)PrintSystemParameters(); // print (log) binary system parameters return evolutionStatus; } diff --git a/src/BaseBinaryStar.h b/src/BaseBinaryStar.h index 856bae625..d6a416351 100644 --- a/src/BaseBinaryStar.h +++ b/src/BaseBinaryStar.h @@ -97,7 +97,8 @@ class BaseBinaryStar { m_SupernovaState = p_Star.m_SupernovaState; - m_SynchronizationTimescale = p_Star.m_SynchronizationTimescale; + m_SynchronizationTimescale1 = p_Star.m_SynchronizationTimescale1; + m_SynchronizationTimescale2 = p_Star.m_SynchronizationTimescale2; m_SystemicVelocity = p_Star.m_SystemicVelocity; m_NormalizedOrbitalAngularMomentumVector = p_Star.m_NormalizedOrbitalAngularMomentumVector; @@ -179,6 +180,12 @@ class BaseBinaryStar { bool HasStarsTouching() const { return (utils::Compare(m_SemiMajorAxis, 0.0) > 0) && (m_SemiMajorAxis <= RSOL_TO_AU * (m_Star1->Radius() + m_Star2->Radius())); } bool HasTwoOf(STELLAR_TYPE_LIST p_List) const; bool ImmediateRLOFPostCEE() const { return m_RLOFDetails.immediateRLOFPostCEE; } + DBL_DBL_DBL_DBL ImKnm1_tidal() const { return m_Star1->CalculateImKnmTidal(OrbitalAngularVelocity(), m_SemiMajorAxis, m_Star2->Mass()); } + DBL_DBL_DBL_DBL ImKnm2_tidal() const { return m_Star2->CalculateImKnmTidal(OrbitalAngularVelocity(), m_SemiMajorAxis, m_Star1->Mass());} + DBL_DBL_DBL_DBL ImKnm1_tidal_eq() const { return m_Star1->CalculateImKnmEquilibrium(OrbitalAngularVelocity(), m_SemiMajorAxis, m_Star2->Mass()); } + DBL_DBL_DBL_DBL ImKnm2_tidal_eq() const { return m_Star2->CalculateImKnmEquilibrium(OrbitalAngularVelocity(), m_SemiMajorAxis, m_Star1->Mass()); } + DBL_DBL_DBL_DBL ImKnm1_tidal_dyn() const { return m_Star1->CalculateImKnmDynamical(OrbitalAngularVelocity(), m_SemiMajorAxis, m_Star2->Mass()); } + DBL_DBL_DBL_DBL ImKnm2_tidal_dyn() const { return m_Star2->CalculateImKnmDynamical(OrbitalAngularVelocity(), m_SemiMajorAxis, m_Star1->Mass()); } STELLAR_TYPE InitialStellarType1() const { return m_Star1->InitialStellarType(); } STELLAR_TYPE InitialStellarType2() const { return m_Star2->InitialStellarType(); } bool IsHMXRBinary() const; @@ -226,6 +233,7 @@ class BaseBinaryStar { double RocheLobeRadius2() const { return CalculateRocheLobeRadius_Static(m_Star2->Mass(), m_Star1->Mass()) * SemiMajorAxisRsol() * (1-Eccentricity()); } double StarToRocheLobeRadiusRatio1() const { return m_Star1->StarToRocheLobeRadiusRatio(m_SemiMajorAxis, m_Eccentricity); } double StarToRocheLobeRadiusRatio2() const { return m_Star2->StarToRocheLobeRadiusRatio(m_SemiMajorAxis, m_Eccentricity); } + double SemiMajorAxisAfterStage1CEE() const { return m_CEDetails.postCEE.semiMajorAxisAfterStage1; } double SemiMajorAxisAtDCOFormation() const { return m_SemiMajorAxisAtDCOFormation; } double SemiMajorAxisInitial() const { return m_SemiMajorAxisInitial; } double SemiMajorAxisPostCEE() const { return m_CEDetails.postCEE.semiMajorAxis; } @@ -245,7 +253,8 @@ class BaseBinaryStar { STELLAR_TYPE StellarType2PreCEE() const { return m_Star2->StellarTypePreCEE(); } double SN_OrbitInclinationAngle() const { return m_ThetaE; } SN_STATE SN_State() const { return m_SupernovaState; } - double SynchronizationTimescale() const { return m_SynchronizationTimescale; } + double SynchronizationTimescale1() const { return m_SynchronizationTimescale1; } + double SynchronizationTimescale2() const { return m_SynchronizationTimescale2; } double SystemicSpeed() const { return m_SystemicVelocity.Magnitude(); } double SystemicVelocityX() const { return m_SystemicVelocity.xValue(); } double SystemicVelocityY() const { return m_SystemicVelocity.yValue(); } @@ -301,7 +310,7 @@ class BaseBinaryStar { BinaryCEDetailsT m_CEDetails; // Common Event details double m_CircularizationTimescale; - + bool m_Unbound; // Binary unbound? double m_Dt; // Timestep @@ -327,7 +336,7 @@ class BaseBinaryStar { double m_FractionAccreted; // Fraction of mass accreted from the donor during mass transfer double m_CosIPrime; - double m_IPrime; + double m_IPrime; double m_JLoss; // Specific angular momentum with which mass is lost during non-conservative mass transfer @@ -344,7 +353,7 @@ class BaseBinaryStar { bool m_MassTransfer; double m_aMassTransferDiff; - MASS_TRANSFER_TIMESCALE m_MassTransferTimescale; + MT_TIMESCALE m_MassTransferTimescale; MT_TRACKING m_MassTransferTrackerHistory; @@ -360,7 +369,8 @@ class BaseBinaryStar { SN_STATE m_SupernovaState; // Indicates which star (or stars) are undergoing / have undergone a supernova event - double m_SynchronizationTimescale; + double m_SynchronizationTimescale1; + double m_SynchronizationTimescale2; Vector3d m_SystemicVelocity; // Systemic velocity vector, relative to ZAMS Center of Mass Vector3d m_NormalizedOrbitalAngularMomentumVector; // Orbital AM vector postSN, in preSN frame @@ -390,6 +400,10 @@ class BaseBinaryStar { double m_ZetaLobe; double m_ZetaStar; + // thresholds flags for system detailed output file + DBL_VECTOR m_SystemSnapshotAgeFlags1; + DBL_VECTOR m_SystemSnapshotAgeFlags2; + BOOL_VECTOR m_SystemSnapshotTimeFlags; // Binaries contain two stars BinaryConstituentStar *m_Star1; // Initially more massive star - the primary @@ -418,16 +432,16 @@ class BaseBinaryStar { void CalculateGravitationalRadiation(); void EmitGravitationalWave(const double p_Dt); - double ChooseTimestep(const double p_Multiplier); + double ChooseTimestep(const double p_Factor = 1.0); void CalculateEnergyAndAngularMomentum(); - double CalculateDEccentricityTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star); - double CalculateDOmegaTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star); - double CalculateDSemiMajorAxisTidalDt(const DBL_DBL_DBL_DBL p_ImKlm, const BinaryConstituentStar* p_Star); + double CalculateDEccentricityTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star); + double CalculateDOmegaTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star); + double CalculateDSemiMajorAxisTidalDt(const DBL_DBL_DBL_DBL p_ImKnm, const BinaryConstituentStar* p_Star); - static double CalculateGammaAngularMomentumLoss_Static(const double p_DonorMass, const double p_AccretorMass, const bool p_IsAccretorDegenerate); - double CalculateGammaAngularMomentumLoss(const double p_DonorMass, const double p_AccretorMass) { return CalculateGammaAngularMomentumLoss_Static(p_DonorMass, p_AccretorMass, m_Accretor->IsDegenerate()); } + static double CalculateGammaAngularMomentumLoss_Static(const double p_DonorMass, const double p_AccretorMass, const bool p_IsAccretorDegenerate, const bool p_IsCommonEnvelope); + double CalculateGammaAngularMomentumLoss(const double p_DonorMass, const double p_AccretorMass) { return CalculateGammaAngularMomentumLoss_Static(p_DonorMass, p_AccretorMass, m_Accretor->IsDegenerate(), false); } double CalculateGammaAngularMomentumLoss() { return CalculateGammaAngularMomentumLoss(m_Donor->Mass(), m_Accretor->Mass()); } @@ -437,13 +451,15 @@ class BaseBinaryStar { const double p_DeltaMassDonor, const double p_AccretorMass, const bool p_IsAccretorDegenerate, - const double p_FractionAccreted); + const double p_FractionAccreted, + const bool p_IsCommonEnvelope); double CalculateMassTransferOrbit(const double p_DonorMass, const double p_DeltaMassDonor, BinaryConstituentStar& p_Accretor, - const double p_FractionAccreted) { return CalculateMassTransferOrbit(p_DonorMass, p_DeltaMassDonor, p_Accretor.Mass(), p_Accretor.IsDegenerate(), p_FractionAccreted); } + const double p_FractionAccreted, + const bool p_IsCommonEnvelope) { return CalculateMassTransferOrbit(p_DonorMass, p_DeltaMassDonor, p_Accretor.Mass(), p_Accretor.IsDegenerate(), p_FractionAccreted, p_IsCommonEnvelope); } @@ -496,6 +512,7 @@ class BaseBinaryStar { void SetRemainingValues(); void SetPostCEEValues(const double p_SemiMajorAxis, + const double p_SemiMajorAxisAfterStage1, const double p_Eccentricity, const double p_RocheLobe1to2, const double p_RocheLobe2to1); @@ -527,10 +544,14 @@ class BaseBinaryStar { bool PrintRLOFParameters(const RLOF_RECORD_TYPE p_RecordType = RLOF_RECORD_TYPE::DEFAULT); - bool PrintBinarySystemParameters(const BSE_SYSPARMS_RECORD_TYPE p_RecordType = BSE_SYSPARMS_RECORD_TYPE::DEFAULT) const { + bool PrintSystemParameters(const BSE_SYSPARMS_RECORD_TYPE p_RecordType = BSE_SYSPARMS_RECORD_TYPE::DEFAULT) const { return LOGGING->LogBSESystemParameters(this, p_RecordType); } + bool PrintSystemSnapshotLog(const BSE_SYSTEM_SNAPSHOT_RECORD_TYPE p_RecordType = BSE_SYSTEM_SNAPSHOT_RECORD_TYPE::DEFAULT) const { + return LOGGING->LogBSESystemSnapshotLog(this, p_RecordType); + } + bool PrintDetailedOutput(const long int p_Id, const BSE_DETAILED_RECORD_TYPE p_RecordType) const { return OPTIONS->DetailedOutput() ? LOGGING->LogBSEDetailedOutput(this, p_Id, p_RecordType) : true; } @@ -543,7 +564,7 @@ class BaseBinaryStar { return LOGGING->LogCommonEnvelope(this, p_RecordType); } - bool PrintPulsarEvolutionParameters(const BSE_PULSAR_RECORD_TYPE p_RecordType = BSE_PULSAR_RECORD_TYPE::DEFAULT) const { + bool PrintPulsarEvolutionParameters(const BSE_PULSAR_RECORD_TYPE p_RecordType) const { return OPTIONS->EvolvePulsars() ? LOGGING->LogBSEPulsarEvolutionParameters(this, p_RecordType) : true; } @@ -556,13 +577,13 @@ class BaseBinaryStar { * * * Constructor: initialise the class - * template RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_MaximumAccretedMass, ERROR *p_Error) + * template RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_Dt, ERROR *p_Error) * * @param [IN] p_Binary (Pointer to) The binary star under examination * @param [IN] p_Donor (Pointer to) The star donating mass * @param [IN] p_Accretor (Pointer to) The star accreting mass - * @param [IN] p_FractionAccreted The fraction of the donated mass accreted by the accretor (for thermal timescale accretion) - * @param [IN] p_MaximumAccretedMass The total amount of mass that can be accreted (for nuclear timescale accretion, p_FractionAccreted should be negative for this to be used) + * @param [IN] p_FractionAccreted The fraction of the donated mass accreted by the accretor (if known in advance, otherwise zero) + * @param [IN] p_Dt Time step duration (relevant for nuclear timescale mass transfer) * @param [IN] p_Error (Address of variable to record) Error encountered in functor * * Function: calculate radius difference after mass loss @@ -573,13 +594,13 @@ class BaseBinaryStar { */ template struct RadiusEqualsRocheLobeFunctor { - RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_MaximumAccretedMass, ERROR *p_Error) { + RadiusEqualsRocheLobeFunctor(BaseBinaryStar *p_Binary, BinaryConstituentStar *p_Donor, BinaryConstituentStar *p_Accretor, double p_FractionAccreted, double p_Dt, ERROR *p_Error) { m_Binary = p_Binary; m_Donor = p_Donor; m_Accretor = p_Accretor; m_Error = p_Error; m_FractionAccreted = p_FractionAccreted; - m_MaximumAccretedMass = p_MaximumAccretedMass; + m_Dt = p_Dt; } T operator()(double const& p_dM) { @@ -590,14 +611,20 @@ class BaseBinaryStar { double donorMass = m_Donor->Mass(); double accretorMass = m_Accretor->Mass(); + // use stale value of accretor RL radius -- this is only relevant for nuclear timescale MT, when the change in accretor RL radius should be small + double accretorRLradius = CalculateRocheLobeRadius_Static(accretorMass, donorMass) * AU_TO_RSOL * m_Binary->SemiMajorAxis() * (1.0 - m_Binary->Eccentricity()); // beta is the actual accretion efficiency; if p_FractionAccreted is negative (placeholder // for nuclear timescale accretion efficiency, for which the total accretion mass over the - // duration of the timestep is known), then the ratio of the maximum allowed accreted - // mass / donated mass is used - double beta = (utils::Compare(m_FractionAccreted, 0.0) >=0 ) ? m_FractionAccreted : std::min(m_MaximumAccretedMass/p_dM, 1.0); + // duration of the timestep is known), then must estimate it on the fly for consistency + double beta = m_FractionAccreted; + if (utils::Compare(beta, 0.0) < 0) { + std::tie(std::ignore, beta) = m_Accretor->CalculateMassAcceptanceRate(p_dM / m_Dt, + m_Accretor->CalculateThermalMassAcceptanceRate(accretorRLradius), + m_Donor->IsOneOf(He_RICH_TYPES)); + } - double semiMajorAxis = m_Binary->CalculateMassTransferOrbit(donorMass, -p_dM , *m_Accretor, beta); + double semiMajorAxis = m_Binary->CalculateMassTransferOrbit(donorMass, -p_dM , *m_Accretor, beta, false); double RLRadius = semiMajorAxis * (1.0 - m_Binary->Eccentricity()) * CalculateRocheLobeRadius_Static(donorMass - p_dM, accretorMass + (beta * p_dM)) * AU_TO_RSOL; double radiusAfterMassLoss = m_Donor->CalculateRadiusOnMassChange(-p_dM); @@ -610,7 +637,7 @@ class BaseBinaryStar { BinaryConstituentStar *m_Accretor; ERROR *m_Error; double m_FractionAccreted; - double m_MaximumAccretedMass; + double m_Dt; }; diff --git a/src/BaseStar.cpp b/src/BaseStar.cpp index 649ad6ace..8220f1bee 100755 --- a/src/BaseStar.cpp +++ b/src/BaseStar.cpp @@ -91,10 +91,10 @@ BaseStar::BaseStar(const unsigned long int p_RandomSeed, // calculate coefficients, constants etc. - CalculateRCoefficients(LogMetallicityXi(), m_RCoefficients); - CalculateLCoefficients(LogMetallicityXi(), m_LCoefficients); + CalculateRCoefficients(LogMetallicityXiHurley(), m_RCoefficients); + CalculateLCoefficients(LogMetallicityXiHurley(), m_LCoefficients); - CalculateMassCutoffs(m_Metallicity, LogMetallicityXi(), m_MassCutoffs); + CalculateMassCutoffs(m_Metallicity, LogMetallicityXiHurley(), m_MassCutoffs); CalculateAnCoefficients(m_AnCoefficients, m_LConstants, m_RConstants, m_GammaConstants); CalculateBnCoefficients(m_BnCoefficients); @@ -107,6 +107,7 @@ BaseStar::BaseStar(const unsigned long int p_RandomSeed, // initialise remaining member variables // Zero age main sequence parameters + m_InitialMainSequenceCoreMass = DEFAULT_INITIAL_DOUBLE_VALUE; // initialised in MS_gt_07 class if BRCEK core mass prescription is used m_RZAMS = CalculateRadiusAtZAMS(m_MZAMS); m_LZAMS = CalculateLuminosityAtZAMS(m_MZAMS); m_TZAMS = CalculateTemperatureOnPhase_Static(m_LZAMS, m_RZAMS); @@ -140,7 +141,7 @@ BaseStar::BaseStar(const unsigned long int p_RandomSeed, m_OmegaCHE = CalculateOmegaCHE(m_MZAMS, m_Metallicity); m_OmegaZAMS = p_RotationalFrequency >= 0.0 // valid rotational frequency passed in? - ? p_RotationalFrequency // yes - use it + ? _2_PI * p_RotationalFrequency // yes - convert from cycles/yr to rad/yr and use it : CalculateZAMSAngularFrequency(m_MZAMS, m_RZAMS); // no - calculate it m_AngularMomentum = CalculateMomentOfInertiaAU() * m_OmegaZAMS; @@ -286,9 +287,11 @@ COMPAS_VARIABLE BaseStar::StellarPropertyValue(const T_ANY_PROPERTY p_Property) case ANY_STAR_PROPERTY::BINDING_ENERGY_LOVERIDGE: value = CalculateBindingEnergy(CalculateLambdaLoveridge()); break; case ANY_STAR_PROPERTY::BINDING_ENERGY_LOVERIDGE_WINDS: value = CalculateBindingEnergy(CalculateLambdaLoveridge(m_Mass - m_CoreMass, true)); break; case ANY_STAR_PROPERTY::BINDING_ENERGY_KRUCKOW: value = CalculateBindingEnergy(CalculateLambdaKruckow()); break; + case ANY_STAR_PROPERTY::BINDING_ENERGY_CONVECTIVE_ENVELOPE: value = CalculateConvectiveEnvelopeBindingEnergy(CalculateConvectiveEnvelopeLambdaPicker(CalculateConvectiveEnvelopeMass())); break; case ANY_STAR_PROPERTY::CHEMICALLY_HOMOGENEOUS_MAIN_SEQUENCE: value = CHonMS(); break; case ANY_STAR_PROPERTY::CO_CORE_MASS: value = COCoreMass(); break; case ANY_STAR_PROPERTY::CO_CORE_MASS_AT_COMPACT_OBJECT_FORMATION: value = SN_COCoreMassAtCOFormation(); break; + case ANY_STAR_PROPERTY::CONVECTIVE_ENV_MASS: std::tie(value, std::ignore) = CalculateConvectiveEnvelopeMass(); break; case ANY_STAR_PROPERTY::CORE_MASS: value = CoreMass(); break; case ANY_STAR_PROPERTY::CORE_MASS_AT_COMPACT_OBJECT_FORMATION: value = SN_CoreMassAtCOFormation(); break; case ANY_STAR_PROPERTY::CORE_RADIUS_AT_COMPACT_OBJECT_FORMATION: value = SN_CoreRadiusAtCOFormation(); break; @@ -332,6 +335,7 @@ COMPAS_VARIABLE BaseStar::StellarPropertyValue(const T_ANY_PROPERTY p_Property) case ANY_STAR_PROPERTY::IS_SNIA: value = IsSNIA(); break; case ANY_STAR_PROPERTY::IS_USSN: value = IsUSSN(); break; case ANY_STAR_PROPERTY::KICK_MAGNITUDE: value = SN_KickMagnitude(); break; + case ANY_STAR_PROPERTY::LAMBDA_CONVECTIVE_ENVELOPE: value = CalculateConvectiveEnvelopeLambdaPicker(CalculateConvectiveEnvelopeMass()); break; case ANY_STAR_PROPERTY::LAMBDA_DEWI: value = CalculateLambdaDewi(); break; case ANY_STAR_PROPERTY::LAMBDA_FIXED: value = OPTIONS->CommonEnvelopeLambda(); break; case ANY_STAR_PROPERTY::LAMBDA_KRUCKOW: value = CalculateLambdaKruckow(); break; @@ -383,6 +387,9 @@ COMPAS_VARIABLE BaseStar::StellarPropertyValue(const T_ANY_PROPERTY p_Property) case ANY_STAR_PROPERTY::TOTAL_RADIUS_AT_COMPACT_OBJECT_FORMATION: value = SN_TotalRadiusAtCOFormation(); break; case ANY_STAR_PROPERTY::TRUE_ANOMALY: value = SN_TrueAnomaly(); break; case ANY_STAR_PROPERTY::TZAMS: value = TZAMS() * TSOL; break; + case ANY_STAR_PROPERTY::VELOCITY_X: value = VelocityX(); break; + case ANY_STAR_PROPERTY::VELOCITY_Y: value = VelocityY(); break; + case ANY_STAR_PROPERTY::VELOCITY_Z: value = VelocityZ(); break; case ANY_STAR_PROPERTY::ZETA_HURLEY: value = CalculateZetaAdiabaticHurley2002(m_CoreMass); break; case ANY_STAR_PROPERTY::ZETA_HURLEY_HE: value = CalculateZetaAdiabaticHurley2002(m_HeCoreMass); break; case ANY_STAR_PROPERTY::ZETA_SOBERMAN: value = CalculateZetaAdiabaticSPH(m_CoreMass); break; @@ -502,7 +509,7 @@ void BaseStar::CalculateAnCoefficients(DBL_VECTOR &p_AnCoefficients, #define GammaConstants(x) p_GammaConstants[static_cast(GAMMA_CONSTANTS::x)] // for convenience and readability - undefined at end of function double Z = m_Metallicity; - double xi = LogMetallicityXi(); + double xi = LogMetallicityXiHurley(); double sigma = LogMetallicitySigma(); // pow() is slow - use multiplication @@ -526,13 +533,16 @@ void BaseStar::CalculateAnCoefficients(DBL_VECTOR &p_AnCoefficients, a[19] *= a[20]; a[29] = PPOW(a[29], (a[32])); a[33] = min(1.4, 1.5135 + (0.3769 * xi)); + a[33] = max(0.6355 - (0.4192 * xi), max(1.25, a[33])); a[42] = min(1.25, max(1.1, a[42])); a[44] = min(1.3, max(0.45, a[44])); a[49] = max(a[49], 0.145); a[50] = min(a[50], (0.306 + (0.053 * xi))); a[51] = min(a[51], (0.3625 + (0.062 * xi))); - a[52] = (utils::Compare(Z, 0.01) > 0) ? min(a[52], 1.0) : max(a[52], 0.9); - a[53] = (utils::Compare(Z, 0.01) > 0) ? min(a[53], 1.1) : max(a[53], 1.0); + a[52] = max(a[52], 0.9); + a[52] = (utils::Compare(Z, 0.01) > 0) ? min(a[52], 1.0) : a[52]; + a[53] = max(a[53], 1.0); + a[53] = (utils::Compare(Z, 0.01) > 0) ? min(a[53], 1.1) : a[53]; a[57] = min(1.4, a[57]); a[57] = max((0.6355 - (0.4192 * xi)), max(1.25, a[57])); a[62] = max(0.065, a[62]); @@ -568,8 +578,8 @@ void BaseStar::CalculateAnCoefficients(DBL_VECTOR &p_AnCoefficients, RConstants(C_BETA_R) = (a[69] * 16384.0) / (a[70] + PPOW(16.0, a[71])); // Hurley et al. 2000, eq 22a RConstants(B_DELTA_R) = (a[38] + a[39] * 8.0 * M_SQRT2) / (a[40] * 8.0 + PPOW(2.0, a[41])) - 1.0; // Hurley et al. 2000, eq 17 - GammaConstants(B_GAMMA) = a[76] + (a[77] * PPOW((1.0 - a[78]), a[79])); // Hurley et al. 2000, eq 23 - GammaConstants(C_GAMMA) = (utils::Compare(a[75], 1.0) == 0) ? GammaConstants(B_GAMMA) : a[80]; // Hurley et al. 2000, eq 23 + GammaConstants(B_GAMMA) = max(0.0, a[76] + (a[77] * PPOW((1.0 - a[78]), a[79]))); // Hurley et al. 2000, eq 23 and discussion immediately following - max() confirmed in BSE Fortran code + GammaConstants(C_GAMMA) = (utils::Compare(a[75], 1.0) <= 0) ? GammaConstants(B_GAMMA) : a[80]; // Hurley et al. 2000, eq 23 and discussion immediately following - <= 1.0 confirmed in BSE Fortran code #undef GammaConstants #undef RConstants @@ -602,7 +612,7 @@ void BaseStar::CalculateBnCoefficients(DBL_VECTOR &p_BnCoefficients) { double Z = m_Metallicity; - double xi = LogMetallicityXi(); + double xi = LogMetallicityXiHurley(); double sigma = LogMetallicitySigma(); double rho = LogMetallicityRho(); @@ -626,8 +636,8 @@ void BaseStar::CalculateBnCoefficients(DBL_VECTOR &p_BnCoefficients) { b[1] = min(0.54, b[1]); b[2] = PPOW(10.0, (-4.6739 - (0.9394 * sigma))); b[2] = min(max(b[2], (-0.04167 + (55.67 * Z))), (0.4771 - (9329.21 * PPOW(Z, 2.94)))); - b[3] = max(-0.1451, (-2.2794 - (1.5175 * sigma) - (0.254 * sigma * sigma))); - b[3] = (utils::Compare(Z, 0.004) > 0) ? max(b[3], 0.7307 + (14265.1 * PPOW(Z, 3.395))) : PPOW(10.0, b[3]); + b[3] = PPOW(10.0, max(-0.1451, (-2.2794 - (1.5175 * sigma) - (0.254 * sigma * sigma)))); + b[3] = (utils::Compare(Z, 0.004) > 0) ? max(b[3], 0.7307 + (14265.1 * PPOW(Z, 3.395))) : b[3]; b[4] += 0.1231572 * xi_5; b[6] += 0.01640687 * xi_5; b[11] = b[11] * b[11]; @@ -849,8 +859,8 @@ void BaseStar::CalculateMassCutoffs(const double p_Metallicity, const double p_L massCutoffs(MHook) = 1.0185 + (0.16015 * p_LogMetallicityXi) + (0.0892 * xi_2); // MHook - Hurley et al. 2000, eq 1 massCutoffs(MHeF) = 1.995 + (0.25 * p_LogMetallicityXi) + (0.087 * xi_2); // MHeF - Hurley et al. 2000, eq 2 - double top = 13.048 * PPOW((p_Metallicity / ZSOL), 0.06); - double bottom = 1.0 + (0.0012 * PPOW((ZSOL / p_Metallicity), 1.27)); + double top = 13.048 * PPOW((p_Metallicity / ZSOL_HURLEY), 0.06); + double bottom = 1.0 + (0.0012 * PPOW((ZSOL_HURLEY / p_Metallicity), 1.27)); massCutoffs(MFGB) = top / bottom; // MFGB - Hurley et al. 2000, eq 3 massCutoffs(MCHE) = 100.0; // MCHE - Mandel/Butler - CHE calculation @@ -875,7 +885,7 @@ void BaseStar::CalculateMassCutoffs(const double p_Metallicity, const double p_L double BaseStar::CalculateGBRadiusXExponent() const { // pow()is slow - use multiplication - double xi = LogMetallicityXi(); + double xi = LogMetallicityXiHurley(); double xi_2 = xi * xi; double xi_3 = xi_2 * xi; double xi_4 = xi_2 * xi_2; @@ -1553,7 +1563,7 @@ double BaseStar::CalculateMassLossRateNieuwenhuijzenDeJager() const { if (utils::Compare(m_Luminosity, NJ_MINIMUM_LUMINOSITY) > 0) { // check for minimum luminosity double smoothTaper = min(1.0, (m_Luminosity - 4000.0) / 500.0); // smooth taper between no mass loss and mass loss - rate = std::sqrt((m_Metallicity / ZSOL)) * smoothTaper * 9.6E-15 * PPOW(m_Radius, 0.81) * PPOW(m_Luminosity, 1.24) * PPOW(m_Mass, 0.16); + rate = std::sqrt((m_Metallicity / ZSOL_HURLEY)) * smoothTaper * 9.6E-15 * PPOW(m_Radius, 0.81) * PPOW(m_Luminosity, 1.24) * PPOW(m_Mass, 0.16); } else { rate = 0.0; } @@ -1799,7 +1809,7 @@ double BaseStar::CalculateMassLossRateWolfRayetZDependent(const double p_Mu) con // TW - Haven't seen StarTrack but I think H&K gives the original equation and V&dK gives the Z dependence double rate = 0.0; if (utils::Compare(p_Mu, 1.0) < 0) { - rate = 1.0E-13 * PPOW(m_Luminosity, 1.5) * PPOW(m_Metallicity / ZSOL, 0.86) * (1.0 - p_Mu); + rate = 1.0E-13 * PPOW(m_Luminosity, 1.5) * PPOW(m_Metallicity / ZSOL_ANDERS, 0.86) * (1.0 - p_Mu); } return rate; } @@ -1823,14 +1833,14 @@ double BaseStar::CalculateMassLossRateOBVink2001() const { double teff = m_Temperature * TSOL; if (utils::Compare(teff, VINK_MASS_LOSS_MINIMUM_TEMP) >= 0 && utils::Compare(teff, VINK_MASS_LOSS_BISTABILITY_TEMP) <= 0) { - double v = 1.3; // v_inf/v_esc - v = v * PPOW(m_Metallicity / ZSOL, OPTIONS->ScaleTerminalWindVelocityWithMetallicityPower()); // Scale Vinf with metallicity + double v = 1.3; // v_inf/v_esc + v = v * PPOW(m_Metallicity / ZSOL_ANDERS, OPTIONS->ScaleTerminalWindVelocityWithMetallicityPower()); // Scale Vinf with metallicity double logMdotOB = -6.688 + (2.210 * log10(m_Luminosity / 1.0E5)) - (1.339 * log10(m_Mass / 30.0)) - (1.601 * log10(v / 2.0)) + - (0.85 * LogMetallicityXi()) + + (0.85 * LogMetallicityXiAnders()) + (1.07 * log10(teff / 20000.0)); rate = PPOW(10.0, logMdotOB); @@ -1839,14 +1849,14 @@ double BaseStar::CalculateMassLossRateOBVink2001() const { else if (utils::Compare(teff, VINK_MASS_LOSS_BISTABILITY_TEMP) > 0) { SHOW_WARN_IF(utils::Compare(teff, VINK_MASS_LOSS_MAXIMUM_TEMP) > 0, ERROR::HIGH_TEFF_WINDS); // show warning if winds being used outside comfort zone - double v = 2.6; // v_inf/v_esc - v = v * PPOW(m_Metallicity / ZSOL, OPTIONS->ScaleTerminalWindVelocityWithMetallicityPower()); // Scale Vinf with metallicity + double v = 2.6; // v_inf/v_esc + v = v * PPOW(m_Metallicity / ZSOL_ANDERS, OPTIONS->ScaleTerminalWindVelocityWithMetallicityPower()); // Scale Vinf with metallicity double logMdotOB = -6.697 + (2.194 * log10(m_Luminosity / 1.0E5)) - (1.313 * log10(m_Mass / 30.0)) - (1.226 * log10(v / 2.0)) + - (0.85 * LogMetallicityXi()) + + (0.85 * LogMetallicityXiAnders()) + (0.933 * log10(teff / 40000.0)) - (10.92 * log10(teff / 40000.0) * log10(teff/40000.0)); @@ -1881,7 +1891,7 @@ double BaseStar::CalculateMassLossRateOBVinkSander2021() const { double teff = m_Temperature * TSOL; double Gamma = EDDINGTON_PARAMETER_FACTOR * m_Luminosity / m_Mass; - double charrho = -14.94 + (3.1857 * Gamma) + (zExp * LogMetallicityXi()); + double charrho = -14.94 + (3.1857 * Gamma) + (zExp * LogMetallicityXiAnders()); double T2 = ( 61.2 + (2.59 * charrho) ) * 1000.0; // typically around 25000.0, higher jump first as in Vink python recipe double T1 = ( 100.0 + (6.0 * charrho) ) * 1000.0; // typically around 20000.0, has similar behavior when fixed @@ -1897,7 +1907,7 @@ double BaseStar::CalculateMassLossRateOBVinkSander2021() const { (2.210 * logL5) - (1.339 * logM30) - (1.601 * log10(V / 2.0)) + - (zExp2001 * LogMetallicityXi()) + + (zExp2001 * LogMetallicityXiAnders()) + (1.07 * logT20); rate = PPOW(10.0, logMdotOB); @@ -1910,7 +1920,7 @@ double BaseStar::CalculateMassLossRateOBVinkSander2021() const { (2.210 * logL5) - (1.339 * logM30) - (1.601 * log10(V / 2.0)) + - (zExp2001 * LogMetallicityXi()) + + (zExp2001 * LogMetallicityXiAnders()) + (1.07 * logT20); rate = PPOW(10.0, logMdotOB); @@ -1923,7 +1933,7 @@ double BaseStar::CalculateMassLossRateOBVinkSander2021() const { (2.194 * logL5) - (1.313 * logM30) - (1.226 * log10(V / 2.0)) + - (zExp * LogMetallicityXi()) + + (zExp * LogMetallicityXiAnders()) + (0.933 * logT40) - (10.92 * logT40 * logT40); @@ -1948,8 +1958,8 @@ double BaseStar::CalculateMassLossRateOBVinkSander2021() const { * @return Mass loss rate for hot OB stars in Msol yr^-1 */ double BaseStar::CalculateMassLossRateOBKrticka2018() const { - - double logMdot = -5.70 + 0.50 * LogMetallicityXi() + (1.61 - 0.12 * LogMetallicityXi()) * log10(m_Luminosity / 1.0E6); + + double logMdot = -5.70 + 0.50 * LogMetallicityXiAsplund() + (1.61 - 0.12 * LogMetallicityXiAsplund()) * log10(m_Luminosity / 1.0E6); return PPOW(10.0, logMdot); } @@ -2295,7 +2305,7 @@ double BaseStar::CalculateMassLossRateWolfRayetSanderVink2020(const double p_Mu) if (utils::Compare(p_Mu, 1.0) < 0) { double logL = log10(m_Luminosity); - double logZ = LogMetallicityXi(); + double logZ = LogMetallicityXiAnders(); // Calculate alpha, L0 and Mdot10 double alpha = 0.32 * logZ + 1.4; // Equation 18 in Sander & Vink 2020 @@ -2363,7 +2373,7 @@ double BaseStar::CalculateMassLossRateWolfRayetTemperatureCorrectionSander2023(c */ double BaseStar::CalculateMassLossRateHeliumStarVink2017() const { - double logMdot = -13.3 + (1.36 * log10(m_Luminosity)) + (0.61 * LogMetallicityXi()); // Vink 2017 Eq. 1. + double logMdot = -13.3 + (1.36 * log10(m_Luminosity)) + (0.61 * LogMetallicityXiAnders()); // Vink 2017 Eq. 1. return PPOW(10.0, logMdot); } @@ -2438,8 +2448,9 @@ double BaseStar::CalculateMassLossRateBelczynski2010() { otherWindsRate = CalculateMassLossRateHurley() * OPTIONS->CoolWindMassLossMultiplier(); // apply cool wind mass loss multiplier } else { // hot stars, add Vink et al. 2001 winds (ignoring bistability jump) + otherWindsRate = CalculateMassLossRateOBVink2001(); - m_DominantMassLossRate = MASS_LOSS_TYPE::OB; // set dominant mass loss rate + m_DominantMassLossRate = MASS_LOSS_TYPE::OB; } if (utils::Compare(LBVRate, otherWindsRate) > 0) { // which is dominant? @@ -2462,11 +2473,11 @@ double BaseStar::CalculateMassLossRateBelczynski2010() { * or are added to other wind mass loss if LBV_MASS_LOSS_PRESCRIPTION::HURLEY_ADD is used. * * - * double CalculateMassLossRateMerritt2024() + * double CalculateMassLossRateMerritt2025() * * @return Mass loss rate in Msol per year */ -double BaseStar::CalculateMassLossRateMerritt2024() { +double BaseStar::CalculateMassLossRateMerritt2025() { m_DominantMassLossRate = MASS_LOSS_TYPE::NONE; @@ -2489,7 +2500,7 @@ double BaseStar::CalculateMassLossRateMerritt2024() { } else if (utils::Compare(m_Mass, VMS_MASS_THRESHOLD) >= 0) { // mass at or above VMS winds threshold? otherWindsRate = CalculateMassLossRateVMS(OPTIONS->VMSMassLossPrescription()); // yes - use VMS mass loss rate - m_DominantMassLossRate = MASS_LOSS_TYPE::VMS; // set dominant mass loss rate + m_DominantMassLossRate = MASS_LOSS_TYPE::VMS; // set dominant mass loss rate } else { // otherwise... otherWindsRate = CalculateMassLossRateOB(OPTIONS->OBMassLossPrescription()); // use OB mass loss rate @@ -2519,7 +2530,7 @@ double BaseStar::CalculateMassLossRate() { double mDot = 0.0; // default return value - if (OPTIONS->UseMassLoss()) { // mass loss enabled? + if (OPTIONS->MassLossPrescription() != MASS_LOSS_PRESCRIPTION::ZERO) { // mass loss enabled? // yes double LBVRate; double otherWindsRate; @@ -2543,8 +2554,8 @@ double BaseStar::CalculateMassLossRate() { mDot = CalculateMassLossRateBelczynski2010(); break; - case MASS_LOSS_PRESCRIPTION::MERRITT2024: - mDot = CalculateMassLossRateMerritt2024(); + case MASS_LOSS_PRESCRIPTION::MERRITT2025: + mDot = CalculateMassLossRateMerritt2025(); break; default: // unknown prescription @@ -2558,7 +2569,7 @@ double BaseStar::CalculateMassLossRate() { THROW_ERROR(ERROR::UNKNOWN_MASS_LOSS_PRESCRIPTION); // throw error } - mDot = mDot * OPTIONS->OverallWindMassLossMultiplier(); // apply overall wind mass loss multiplier + mDot *= OPTIONS->OverallWindMassLossMultiplier(); // apply overall wind mass loss multiplier } mDot = min(mDot, MAXIMUM_WIND_MASS_LOSS_RATE); // cap winds at a maximum mass loss rate (typically 0.1 solar masses per year) to avoid convergence issues @@ -2569,33 +2580,6 @@ double BaseStar::CalculateMassLossRate() { } -/* - * Calculate the nuclear mass loss rate as the mass divided by the radial expansion timescale - * We do not use CalculateRadialExpansionTimescale(), however, since in the process of mass transfer the previous radius - * is determined by binary evolution, not nuclear timescale evolution - * - * - * double CalculateNuclearMassLossRate() - * - * @return Nuclear mass loss rate - */ -double BaseStar::CalculateNuclearMassLossRate() { - - // We create and age it slightly to determine how the radius will change. - // To be sure the clone does not participate in logging, we set its persistence to EPHEMERAL. - BaseStar *clone = Clone(OBJECT_PERSISTENCE::EPHEMERAL, false); // do not re-initialise the clone - - double timestep = std::max(1000.0 * NUCLEAR_MINIMUM_TIMESTEP, m_Age / 1.0E6); - clone->UpdateAttributesAndAgeOneTimestep(0.0, 0.0, timestep, true, false); - double radiusAfterAging = clone->Radius(); - delete clone; clone = nullptr; // return the memory allocated for the clone - - double radialExpansionTimescale = timestep * m_Radius / fabs(m_Radius - radiusAfterAging); - - return m_Mass / radialExpansionTimescale; -} - - /* * Calculate values for mDot and mass assuming mass loss is applied * @@ -2618,8 +2602,8 @@ double BaseStar::CalculateMassLossValues(double p_Dt, const bool p_UpdateMDot) { double mass = m_Mass; - if (OPTIONS->UseMassLoss()) { // only if using mass loss (program option) - + if (OPTIONS->MassLossPrescription() != MASS_LOSS_PRESCRIPTION::ZERO) { // mass loss enabled? + // yes double mDot = CalculateMassLossRate(); // calculate mass loss rate double massLoss = max(0.0, mDot * p_Dt * 1.0E6); // calculate mass loss; mass loss rate given in Msol per year, times are in Myr so need to multiply by 10^6 if (p_UpdateMDot) m_Mdot = mDot; // update class member variable if necessary @@ -2654,23 +2638,18 @@ double BaseStar::CalculateMassLossValues(double p_Dt, const bool p_UpdateMDot) { */ void BaseStar::ResolveMassLoss(double p_Dt) { - if (OPTIONS->UseMassLoss()) { - + if (OPTIONS->MassLossPrescription() != MASS_LOSS_PRESCRIPTION::ZERO) { // mass loss enabled? + // yes double mass = CalculateMassLossValues(p_Dt, true); // calculate new values assuming mass loss applied double angularMomentumChange = (2.0 / 3.0) * (mass - m_Mass) * m_Radius * RSOL_TO_AU * m_Radius * RSOL_TO_AU * Omega(); // JR: this is here to keep attributes in sync BSE vs SSE - // Supernovae are caught in UpdateAttributesAndAgeOneTimestep() (hence the need to move the - // call to PrintStashedSupernovaDetails() in Star:EvolveOneTimestep()) + // Supernovae are caught in UpdateAttributesAndAgeOneTimestep() // Don't resolve envelope loss here (JR: we're not going to switch anyway... need to revisit this) STELLAR_TYPE st = UpdateAttributesAndAgeOneTimestep(mass - m_Mass, 0.0, 0.0, false, false); // recalculate stellar attributes if (st != m_StellarType) { // should switch? SHOW_WARN(ERROR::SWITCH_NOT_TAKEN); // show warning if we think we should switch again... - - // we may have stashed SN details - need to clear them if we're not going to switch, - // but only if not an ephemeral clone (ephemeral clones don't write to the stash) - if (IsSupernova() && m_ObjectPersistence == OBJECT_PERSISTENCE::PERMANENT) ClearSupernovaStash(); } UpdateInitialMass(); // update effective initial mass (MS, HG & HeMS) @@ -2735,12 +2714,17 @@ DBL_DBL BaseStar::CalculateMassAcceptanceRate(const double p_DonorMassRate, cons switch (OPTIONS->MassTransferAccretionEfficiencyPrescription()) { - case MT_ACCRETION_EFFICIENCY_PRESCRIPTION::THERMALLY_LIMITED: // thermally limited mass transfer: - + case MT_ACCRETION_EFFICIENCY_PRESCRIPTION::THERMALLY_LIMITED: // thermally limited mass transfer acceptanceRate = min(OPTIONS->MassTransferCParameter() * p_AccretorMassRate, p_DonorMassRate); fractionAccreted = acceptanceRate / p_DonorMassRate; break; + case MT_ACCRETION_EFFICIENCY_PRESCRIPTION::HAMSTARS: // thermally limited mass transfer, following Lau+, 2024 + // the mass transfer C parameter is fit using the data from Figure (1) of Lau+, 2024 + acceptanceRate = min(PPOW(10.0, (4.0 / PPOW((Mass() + 0.2), 0.3) - 0.6)) * p_AccretorMassRate, p_DonorMassRate); + fractionAccreted = acceptanceRate / p_DonorMassRate; + break; + case MT_ACCRETION_EFFICIENCY_PRESCRIPTION::FIXED_FRACTION: // fixed fraction of mass accreted, as in StarTrack fractionAccreted = OPTIONS->MassTransferFractionAccreted(); acceptanceRate = min(p_DonorMassRate, fractionAccreted * p_DonorMassRate); @@ -2976,36 +2960,22 @@ double BaseStar::CalculateTemperatureKelvinOnPhase(const double p_Luminosity, co */ double BaseStar::CalculateOStarRotationalVelocityAnalyticCDF_Static(const double p_Ve) { - double alpha = 4.82; - double beta = 1.0 / 25.0; - double mu = 205.0; - double sigma = 190.0; - double iGamma = 0.43; + constexpr double alpha = 4.82; + constexpr double beta = 1.0 / 25.0; + constexpr double mu = 205.0; + constexpr double sigma = 190.0; + constexpr double iGamma = 0.43; boost::math::inverse_gamma_distribution<> gammaComponent(alpha, beta); // (shape, scale) = (alpha, beta) boost::math::normal_distribution<> normalComponent(mu, sigma); + + // Compute CDF at zero rotational velocity -- the CDF should be relative to this quantity + double CDFzero = (iGamma * boost::math::cdf(gammaComponent, 0.0)) + ((1.0 - iGamma) * boost::math::cdf(normalComponent, 0.0)); + + double CDFunnormalised = (iGamma * boost::math::cdf(gammaComponent, p_Ve)) + ((1.0 - iGamma) * boost::math::cdf(normalComponent, p_Ve)); + + return ((CDFunnormalised-CDFzero) / (1.0 - CDFzero)); - return (iGamma * boost::math::cdf(gammaComponent, p_Ve)) + ((1.0 - iGamma) * boost::math::cdf(normalComponent, p_Ve)); -} - - -/* - * Calculate the inverse of the analytic cumulative distribution function (CDF) for the - * equatorial rotational velocity of single O stars. - * - * (i.e. calculate the inverse of CalculateOStarRotationalVelocityAnalyticCDF_Static()) - * - * - * double CalculateOStarRotationalVelocityAnalyticCDFInverse_Static(const double p_Ve, const void *p_Params) - * - * @param [IN] p_vE Rotational velocity (in km s^-1) - value of the kick vk which we want to find - * @param [IN] p_Params Pointer to RotationalVelocityParams structure containing y, the CDF draw U(0,1) - * @return Inverse CDF - * Should be zero when p_Ve = vk, the value of the kick to draw - */ -double BaseStar::CalculateOStarRotationalVelocityAnalyticCDFInverse_Static(double p_Ve, void* p_Params) { - RotationalVelocityParams* params = (RotationalVelocityParams*) p_Params; - return CalculateOStarRotationalVelocityAnalyticCDF_Static(p_Ve) - params->u; } @@ -3018,68 +2988,93 @@ double BaseStar::CalculateOStarRotationalVelocityAnalyticCDFInverse_Static(doubl * Ramirez-Agudelo et al. 2013 https://arxiv.org/abs/1309.2929 * * - * double CalculateOStarRotationalVelocity_Static(const double p_Xmin, const double p_Xmax) + * double CalculateOStarRotationalVelocity * - * @param [IN] p_Xmin Minimum value for root - * @param [IN] p_Xmax Maximum value for root * @return Rotational velocity in km s^-1 */ -double BaseStar::CalculateOStarRotationalVelocity_Static(const double p_Xmin, const double p_Xmax) { - - double xMin = p_Xmin; - double xMax = p_Xmax; - - double result = xMin; - - double maximumInverse = CalculateOStarRotationalVelocityAnalyticCDF_Static(xMax); - double minimumInverse = CalculateOStarRotationalVelocityAnalyticCDF_Static(xMin); - - double rand = RAND->Random(); - - while (utils::Compare(rand, maximumInverse) > 0) { - xMax *= 2.0; - maximumInverse = CalculateOStarRotationalVelocityAnalyticCDF_Static(xMax); - } - - if (utils::Compare(rand, minimumInverse) >= 0) { - - const gsl_root_fsolver_type *T; - gsl_root_fsolver *s; - gsl_function F; - - RotationalVelocityParams params = {rand}; - - F.function = &CalculateOStarRotationalVelocityAnalyticCDFInverse_Static; - F.params = ¶ms; - - // gsl_root_fsolver_brent - // gsl_root_fsolver_bisection - T = gsl_root_fsolver_brent; - s = gsl_root_fsolver_alloc(T); - - gsl_root_fsolver_set(s, &F, xMin, xMax); - - int status = GSL_CONTINUE; - int iter = 0; - int maxIter = 100; - - while (status == GSL_CONTINUE && iter < maxIter) { - iter++; - status = gsl_root_fsolver_iterate(s); - result = gsl_root_fsolver_root(s); - xMin = gsl_root_fsolver_x_lower(s); - xMax = gsl_root_fsolver_x_upper(s); - status = gsl_root_test_interval(xMin, xMax, 0, 0.001); +double BaseStar::CalculateOStarRotationalVelocity() { + + double desiredCDF = RAND->Random(); // Random desired CDF + + const boost::uintmax_t maxit = ADAPTIVE_RV_MAX_ITERATIONS; // Limit to maximum iterations. + boost::uintmax_t it = maxit; // Initially our chosen max iterations, but updated with actual. + + // find root + // we use an iterative algorithm to find the root here: + // - if the root finder throws an exception, we stop and return a negative value for the root (indicating no root found) + // - if the root finder reaches the maximum number of (internal) iterations, we stop and return a negative value for the root (indicating no root found) + // - if the root finder returns a solution, we check that func(solution) = 0.0 +/ ROOT_ABS_TOLERANCE + // - if the solution is acceptable, we stop and return the solution + // - if the solution is not acceptable, we reduce the search step size and try again + // - if we reach the maximum number of search step reduction iterations, or the search step factor reduces to 1.0 (so search step size = 0.0), + // we stop and return a negative value for the root (indicating no root found) + + double guess = 100.0; // guess at 100 km s^-1 (arbitrary initial guess) + + double factorFrac = ADAPTIVE_RV_SEARCH_FACTOR_FRAC; // search step size factor fractional part + double factor = 1.0 + factorFrac; // factor to determine search step size (size = guess * factor) + + std::pair root(-1.0, -1.0); // initialise root - default return + std::size_t tries = 0; // number of tries + bool done = false; // finished (found root or exceed maximum tries)? + ERROR error = ERROR::NONE; + OStarRotationVelocityFunctor func = OStarRotationVelocityFunctor(desiredCDF); + while (!done) { // while no error and acceptable root found + + bool isRising = true; //guess for direction of search; CDF increases monotonically + + // run the root finder + // regardless of any exceptions or errors, display any problems as a warning, then + // check if the root returned is within tolerance - so even if the root finder + // bumped up against the maximum iterations, or couldn't bracket the root, use + // whatever value it ended with and check if it's good enough for us - not finding + // an acceptable root should be the exception rather than the rule, so this strategy + // shouldn't cause undue performance issues. + try { + error = ERROR::NONE; + root = boost::math::tools::bracket_and_solve_root(func, guess, factor, isRising, utils::BracketTolerance, it); // find root + // root finder returned without raising an exception + if (error != ERROR::NONE) { SHOW_WARN(error); } // root finder encountered an error + else if (it >= maxit) { SHOW_WARN(ERROR::TOO_MANY_RV_ITERATIONS); } // too many root finder iterations + } + catch(std::exception& e) { // catch generic boost root finding error + // root finder exception + // could be too many iterations, or unable to bracket root - it may not + // be a hard error - so no matter what the reason is that we are here, + // we'll just emit a warning and keep trying + if (it >= maxit) { SHOW_WARN(ERROR::TOO_MANY_RV_ITERATIONS); } // too many root finder iterations + else { SHOW_WARN(ERROR::ROOT_FINDER_FAILED, e.what()); } // some other problem - show it as a warning } - // JR: should we issue a warning, or throw an error, if the root finder didn't actually find the roor here (i.e. we stopped because pf maxIter)? - // To be consistent, should we use the Boost root solver here? - // **Ilya** both questions above -- IM: yes to both, TBC - - gsl_root_fsolver_free(s); // de-allocate memory for root solver + // we have a solution from the root finder - it may not be an acceptable solution + // so we check if it is within our preferred tolerance + if (fabs(func(root.first + (root.second - root.first) / 2.0)) <= ROOT_ABS_TOLERANCE) { // solution within tolerance? + done = true; // yes - we're done + } + else if (fabs(func(root.first)) <= ROOT_ABS_TOLERANCE) { // solution within tolerance at endpoint 1? + root.second=root.first; + done = true; // yes - we're done + } + else if (fabs(func(root.second)) <= ROOT_ABS_TOLERANCE) { // solution within tolerance at endpoint 2? + root.first=root.second; + done = true; // yes - we're done + } + else { // no - try again + // we don't have an acceptable solution - reduce search step size and try again + factorFrac /= 2.0; // reduce fractional part of factor + factor = 1.0 + factorFrac; // new search step size + tries++; // increment number of tries + if (tries > ADAPTIVE_RV_MAX_TRIES || fabs(factor - 1.0) <= ROOT_ABS_TOLERANCE) { // too many tries, or step size 0.0? + // we've tried as much as we can - fail here with -ve return value + root.first = -1.0; // yes - set error return + root.second = -1.0; + SHOW_WARN(ERROR::TOO_MANY_RV_TRIES); // show warning + done = true; // we're done + } + } } - - return result; + + return root.first + (root.second - root.first) / 2.0; // Midway between brackets is our result, if necessary we could return the result as an interval here. } @@ -3113,16 +3108,18 @@ double BaseStar::CalculateRotationalVelocity(double p_MZAMS) { case ROTATIONAL_VELOCITY_DISTRIBUTION::VLTFLAMES: // VLTFLAMES // Rotational velocity based on VLT-FLAMES survey. - // For O-stars use results of Ramirez-Agudelo et al. (2013) https://arxiv.org/abs/1309.2929 (single stars) + // For O-stars (taken to be above 16 Msol), use results + // of Ramirez-Agudelo et al. (2013) https://arxiv.org/abs/1309.2929 (single stars) // and Ramirez-Agudelo et al. (2015) https://arxiv.org/abs/1507.02286 (spectroscopic binaries) - // For B-stars use results of Dufton et al. (2013) https://arxiv.org/abs/1212.2424 - // For lower mass stars, I don't know what updated results there are so default back to - // Hurley et al. 2000 distribution for now + // For B-stars (taken to be between 2 and 16 Msol) use results + // of Dufton et al. (2013) https://arxiv.org/abs/1212.2424 + // For lower mass stars, default back to Hurley et al. 2000 distribution for now - if (utils::Compare(p_MZAMS, 16.0) >= 0) { // JR: what does 16.0 represent? Not another mass threshold that should be in constants.h ...? /*ilya*/ - vRot = CalculateOStarRotationalVelocity_Static(0.0, 800.0); + if (utils::Compare(p_MZAMS, 16.0) >= 0) { + vRot = CalculateOStarRotationalVelocity(); + vRot = max(vRot, 0.0); // Set to no rotation if no positive solution found; warning already raised } - else if (utils::Compare(p_MZAMS, 2.0) >= 0) { // JR: what does 2.0 represent? Not another mass threshold that should be in constants.h ...? **Ilya** + else if (utils::Compare(p_MZAMS, 2.0) >= 0) { vRot = utils::InverseSampleFromTabulatedCDF(RAND->Random(), BStarRotationalVelocityCDFTable); } else { @@ -3191,7 +3188,7 @@ double BaseStar::CalculateOmegaBreak() const { * * @param [IN] p_MZAMS Zero age main sequence mass in Msol * @param [IN] p_Metallicity Metallicity of the star - * @return Initial angular frequency in rad*s^-1 + * @return Minimum angular frequency in rad*yr^-1 */ double BaseStar::CalculateOmegaCHE(const double p_MZAMS, const double p_Metallicity) const { #define massCutoffs(x) m_MassCutoffs[static_cast(MASS_CUTOFF::x)] // for convenience and readability - undefined at end of function @@ -3219,7 +3216,7 @@ double BaseStar::CalculateOmegaCHE(const double p_MZAMS, const double p_Metallic /* - * Calculate the Dynamical tides contribution to the (l,m) = [(1,0), (1,2), (2,2), (3,2)] imaginary components of the + * Calculate the Dynamical tides contribution to the l=2, (n,m) = [(1,0), (1,2), (2,2), (3,2)] imaginary components of the * potential tidal Love number * * Gravity Waves, Core boundary: @@ -3231,7 +3228,7 @@ double BaseStar::CalculateOmegaCHE(const double p_MZAMS, const double p_Metallic * Inertial Waves, Convective Envelope: * Ogilvie, 2013, Eq. (B3) * - * DBL_DBL_DBL_DBL CalculateImKlmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) + * DBL_DBL_DBL_DBL CalculateImKnmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) * * @param [IN] p_Omega Orbital angular frequency (1/yr) * @param [IN] p_SemiMajorAxis Semi-major axis of binary (AU) @@ -3239,7 +3236,7 @@ double BaseStar::CalculateOmegaCHE(const double p_MZAMS, const double p_Metallic * @return [(1,0), (1,2), (2,2), (3,2)] Imaginary components of the * potential tidal Love number, Dynamical tides only (unitless) */ -DBL_DBL_DBL_DBL BaseStar::CalculateImKlmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { +DBL_DBL_DBL_DBL BaseStar::CalculateImKnmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { double coreMass = CalculateConvectiveCoreMass(); @@ -3254,7 +3251,7 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmDynamical(const double p_Omega, const do double radiusIntershellAU = radiusAU - convectiveEnvRadiusAU; // Outer radial coordinate of radiative intershell // There should be no Dynamical tides if the entire star is convective, i.e. if there are no convective-radiative boundaries. - // If so, return 0.0 for all dynamical components of ImKlm. + // If so, return 0.0 for all dynamical components of ImKnm. // This condition should be true for low-mass MS stars (<= 0.35 Msol) at ZAMS. if (utils::Compare(radIntershellMass/m_Mass, TIDES_MINIMUM_FRACTIONAL_EXTENT) <= 0 || utils::Compare(radiusIntershellAU, coreRadiusAU) <= 0) { return std::make_tuple(0.0, 0.0, 0.0, 0.0); @@ -3286,40 +3283,40 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmDynamical(const double p_Omega, const do // Assume that GW dissipation from core boundary is only efficient if the radiative region extends to the surface, i.e. there is no convective envelope. if (utils::Compare(coreRadiusAU/radiusAU, TIDES_MINIMUM_FRACTIONAL_EXTENT) > 0 && utils::Compare(coreMass/m_Mass, TIDES_MINIMUM_FRACTIONAL_EXTENT) > 0 && utils::Compare(convectiveEnvRadiusAU/radiusAU, TIDES_MINIMUM_FRACTIONAL_EXTENT) < 0 && utils::Compare(envMass/m_Mass, TIDES_MINIMUM_FRACTIONAL_EXTENT) < 0) { - double beta2Dynamical = 1.0; - double rhoFactorDynamcial = 0.1; + constexpr double beta2Dynamical = 1.0; + constexpr double rhoFactorDynamcial = 0.1; double coreRadiusOverRadius = coreRadiusAU / radiusAU; double coreRadiusOverRadius_3 = coreRadiusOverRadius * coreRadiusOverRadius * coreRadiusOverRadius; double coreRadiusOverRadius_9 = coreRadiusOverRadius_3 * coreRadiusOverRadius_3 * coreRadiusOverRadius_3; double massOverCoreMass = m_Mass / coreMass; double E2Dynamical = (2.0 / 3.0) * coreRadiusOverRadius_9 * massOverCoreMass * std::cbrt(massOverCoreMass) * beta2Dynamical * rhoFactorDynamcial; - // (l=1, m=0), Gravity Wave dissipation from core boundary + // (l=2, n=1, m=0), Gravity Wave dissipation from core boundary double s10 = w10 * sqrtR3OverG_M; double s10_4_3 = s10 * std::cbrt(s10); double s10_8_3 = s10_4_3 * s10_4_3; - k10GravityCore = E2Dynamical * (w10 < 0.0 ? -std::abs(s10_8_3) : s10_8_3); + k10GravityCore = E2Dynamical * std::copysign(s10_8_3, w10); if (std::isnan(k10GravityCore)) k10GravityCore = 0.0; - // (l=1, m=2), Gravity Wave dissipation from core boundary + // (l=2, n=1, m=2), Gravity Wave dissipation from core boundary double s12 = w12 * sqrtR3OverG_M; double s12_4_3 = s12 * std::cbrt(s12); double s12_8_3 = s12_4_3 * s12_4_3; - k12GravityCore = E2Dynamical * (w12 < 0.0 ? -std::abs(s12_8_3) : s12_8_3); + k12GravityCore = E2Dynamical * std::copysign(s12_8_3, w12); if (std::isnan(k12GravityCore)) k12GravityCore = 0.0; - // (l=2, m=2), Gravity Wave dissipation from core boundary + // (l=2, n=2, m=2), Gravity Wave dissipation from core boundary double s22 = w22 * sqrtR3OverG_M; double s22_4_3 = s22 * std::cbrt(s22); double s22_8_3 = s22_4_3 * s22_4_3; - k22GravityCore = E2Dynamical * (w22 < 0.0 ? -std::abs(s22_8_3) : s22_8_3); + k22GravityCore = E2Dynamical * std::copysign(s22_8_3, w22); if (std::isnan(k22GravityCore)) k22GravityCore = 0.0; - // (l=3, m=2), Gravity Wave dissipation from core boundary + // (l=2, n=3, m=2), Gravity Wave dissipation from core boundary double s32 = w32 * sqrtR3OverG_M; double s32_4_3 = s32 * std::cbrt(s32); double s32_8_3 = s32_4_3 * s32_4_3; - k32GravityCore = E2Dynamical * (w32 < 0.0 ? -std::abs(s32_8_3) : s32_8_3); + k32GravityCore = E2Dynamical * std::copysign(s32_8_3, w32); if (std::isnan(k32GravityCore)) k32GravityCore = 0.0; } @@ -3330,7 +3327,8 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmDynamical(const double p_Omega, const do // There is no GW or IW dissipation from the envelope boundary if no convective envelope if ((utils::Compare(convectiveEnvRadiusAU / radiusAU, TIDES_MINIMUM_FRACTIONAL_EXTENT) > 0) || (utils::Compare(envMass / m_Mass, TIDES_MINIMUM_FRACTIONAL_EXTENT) > 0)) { - double dynPrefactor = 3.207452512782476; // 3^(11/3) * Gamma(1/3)^2 / 40 PI + constexpr double dynPrefactor = 3.207452512782476; // 3^(11/3) * Gamma(1/3)^2 / 40 PI + constexpr double m_l_factor_22 = 0.183440402716368; // m * (l(l+1))^{-4/3} double cbrtdNdlnr = std::cbrt(G_AU_Msol_yr * radIntershellMass / radiusIntershellAU / (radiusAU - radiusIntershellAU) / (radiusAU - radiusIntershellAU)); double alpha = radiusIntershellAU / radiusAU; @@ -3354,56 +3352,53 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmDynamical(const double p_Omega, const do if (utils::Compare(coreRadiusAU/radiusAU, TIDES_MINIMUM_FRACTIONAL_EXTENT) < 0) { double Epsilon = alpha_11 * envMass / m_Mass * oneMinusGamma_2 * alpha_2_3Minus_1 * alpha_2_3Minus_1 / beta_2 / oneMinusAlpha_3 / oneMinusAlpha_2; - // (l=1, m=0), Gravity Wave dissipation from envelope boundary is always 0.0 since m=0.0 + // (l=2, n=1, m=0), Gravity Wave dissipation from envelope boundary is always 0.0 since m * (l(l+1))^{-4/3} = 0 - // (l=1, m=2), Gravity Wave dissipation from envelope boundary - double m_l_factor_12 = 2.0 / (1.0 * (1.0 + 1.0)) / std::cbrt(1.0 * (1.0 + 1.0)); // m * (l(l+1))^{-4/3} + // (l=2, n=1, m=2), Gravity Wave dissipation from envelope boundary double w12_4_3 = w12 * std::cbrt(w12); double w12_8_3 = w12_4_3 * w12_4_3; - k12GravityEnv = dynPrefactor * m_l_factor_12 * (w12 < 0.0 ? -std::abs(w12_8_3) : w12_8_3) * R3OverG_M * Epsilon / cbrtdNdlnr; + k12GravityEnv = dynPrefactor * m_l_factor_22 * std::copysign(w12_8_3, w12) * R3OverG_M * Epsilon / cbrtdNdlnr; if (std::isnan(k12GravityEnv)) k12GravityEnv = 0.0; - // (l=2, m=2), Gravity Wave dissipation from envelope boundary - double m_l_factor_22 = 2.0 / (2.0 * (2.0 + 1.0)) / std::cbrt(2.0 * (2.0 + 1.0)); // m * (l(l+1))^{-4/3} + // (l=2, n=2, m=2), Gravity Wave dissipation from envelope boundary double w22_4_3 = w22 * std::cbrt(w22); double w22_8_3 = w22_4_3 * w22_4_3; - k22GravityEnv = dynPrefactor * m_l_factor_22 * (w22 < 0.0 ? -std::abs(w22_8_3) : w22_8_3) * R3OverG_M * Epsilon / cbrtdNdlnr; + k22GravityEnv = dynPrefactor * m_l_factor_22 * std::copysign(w22_8_3, w22)* R3OverG_M * Epsilon / cbrtdNdlnr; if (std::isnan(k22GravityEnv)) k22GravityEnv = 0.0; - // (l=3, m=2), Gravity Wave dissipation from envelope boundary - double m_l_factor_32 = 2.0 / (3.0 * (3.0 + 1.0)) / std::cbrt(3.0 * (3.0 + 1.0)); // m * (l(l+1))^{-4/3} + // (l=2, n=3, m=2), Gravity Wave dissipation from envelope boundary double w32_4_3 = w32 * std::cbrt(w32); double w32_8_3 = w32_4_3 * w32_4_3; - k32GravityEnv = dynPrefactor * m_l_factor_32 * (w32 < 0.0 ? -std::abs(w32_8_3) : w32_8_3) * R3OverG_M * Epsilon / cbrtdNdlnr; + k32GravityEnv = dynPrefactor * m_l_factor_22 * std::copysign(w32_8_3, w32) * R3OverG_M * Epsilon / cbrtdNdlnr; if (std::isnan(k32GravityEnv)) k32GravityEnv = 0.0; } - // (l=2, m=2), Inertial Wave dissipation, convective envelope + // (l=2, n=2, m=2), Inertial Wave dissipation, convective envelope // IW dissipation is only efficient for highly spinning stars, as in Esseldeurs, et al., 2024 if (utils::Compare(twoOmegaSpin, p_Omega) >= 0) { double epsilonIW_2 = omegaSpin * omegaSpin * R3OverG_M; - double one_minus_alpha_4 = oneMinusAlpha_2 * oneMinusAlpha_2; + double oneMinusAlpha_4 = oneMinusAlpha_2 * oneMinusAlpha_2; double bracket1 = 1.0 + (2.0 * alpha) + (3.0 * alpha_2) + (3.0 * alpha_3 / 2.0); double bracket2 = 1.0 + (oneMinusGamma / gamma) * alpha_3; double bracket3 = 1.0 + (3.0 * gamma / 2.0) + (5.0 * alpha_3 / (2.0 * gamma) * (1.0 + (gamma / 2.0) - (3.0* gamma * gamma / 2.0))) - (9.0 / 4.0 * oneMinusGamma * alpha_5); - k22InertialEnv = (100.0 * M_PI / 63.0) * epsilonIW_2 * (alpha_5 / (1.0 - alpha_5)) * oneMinusGamma_2 * one_minus_alpha_4 * bracket1 * bracket1 * bracket2 / bracket3 / bracket3; - k22InertialEnv = (w22 < 0.0 ? -std::abs(k22InertialEnv) : std::abs(k22InertialEnv)); + k22InertialEnv = (100.0 * M_PI / 63.0) * epsilonIW_2 * (alpha_5 / (1.0 - alpha_5)) * oneMinusGamma_2 * oneMinusAlpha_4 * bracket1 * bracket1 * bracket2 / bracket3 / bracket3; + k22InertialEnv = std::copysign(k22InertialEnv, w22); if (std::isnan(k22InertialEnv)) k22InertialEnv = 0.0; } } - // return ImKlmDynamical + // return ImKnmDynamical return std::make_tuple(k10GravityCore + k10GravityEnv, k12GravityCore + k12GravityEnv, k22GravityCore + k22GravityEnv + k22InertialEnv, k32GravityCore + k32GravityEnv); } /* - * Calculate the Equilibrium tides contribution to the (l,m) = [(1,0), (1,2), (2,2), (3,2)] imaginary components of the + * Calculate the Equilibrium tides contribution to the l=2, (n,m) = [(1,0), (1,2), (2,2), (3,2)] imaginary components of the * potential tidal Love number * - * Barker (2020), Eqs. (20) to (27), (l=2, m=2 mode only). + * Barker (2020), Eqs. (20) to (27), (l=2, n=2, m=2 mode only). * - * DBL_DBL_DBL_DBL CalculateImKlmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) + * DBL_DBL_DBL_DBL CalculateImKnmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) * * @param [IN] p_Omega Orbital angular frequency (1/yr) * @param [IN] p_SemiMajorAxis Semi-major axis of binary (AU) @@ -3411,7 +3406,7 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmDynamical(const double p_Omega, const do * @return [(1,0), (1,2), (2,2), (3,2)] Imaginary components of the * potential tidal Love number, Equilibrium tides only (unitless) */ -DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { +DBL_DBL_DBL_DBL BaseStar::CalculateImKnmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { // Viscous dissipation // No contribution from convective core; only convective envelope. @@ -3430,20 +3425,12 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const double rOut_5 = rOut_2 * rOut_3; double rOut_7 = rOut_2 * rOut_5; double rOut_9 = rOut_2 * rOut_7; - double rOut_11 = rOut_2 * rOut_9; double rIn_2 = rInAU * rInAU; double rIn_3 = rIn_2 * rInAU; double rIn_5 = rIn_2 * rIn_3; double rIn_7 = rIn_2 * rIn_5; double rIn_9 = rIn_2 * rIn_7; - double rIn_11 = rIn_2 * rIn_9; - - double a_2 = p_SemiMajorAxis * p_SemiMajorAxis; - double a_3 = a_2 * p_SemiMajorAxis; - double a_4 = a_2 * a_2; - double a_6 = a_3 * a_3; - double a_8 = a_6 * a_2; double omegaSpin = Omega(); double twoOmegaSpin = omegaSpin + omegaSpin; @@ -3454,8 +3441,7 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const double vConv = lConv / tConv; double omegaConv = 1.0 / tConv; // absent factor of 2*PI, following Barker (2020) double vl = vConv * lConv; - double m2OverM = p_M2 / m_Mass; - double m2OverM_2 = m2OverM * m2OverM; + double M_2 = m_Mass * m_Mass; double vl_5 = 5.0 * vl; double vl25OverRoot20 = vl * (25.0 / std::sqrt(20.0)); @@ -3466,7 +3452,9 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const double w22 = ((p_Omega + p_Omega) - (twoOmegaSpin)); double w32 = ((p_Omega + p_Omega + p_Omega) - (twoOmegaSpin)); - // (l=1, m=0), Viscous dissipation, convective envelope + double k2_prefactor = (224.0 * M_PI / 15.0) * (rOut_9 - rIn_9) * rhoConv / G_AU_Msol_yr / M_2 / rOut_5; + + // (l=2, n=1, m=0), Viscous dissipation, convective envelope double omega_t_10 = std::abs(w10); double omega_tOverOmega_c_10 = omega_t_10 / omegaConv; double nuTidal10 = vl_5; @@ -3476,15 +3464,12 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const else if (utils::Compare(omega_tOverOmega_c_10, 0.01) > 0) { nuTidal10 = vlOver2 / std::sqrt(omega_tOverOmega_c_10); } - double Dnu10 = (99.0 / 14.0) * omega_t_10 * omega_t_10 * m2OverM_2 * (rOut_7 - rIn_7) * rhoConv * nuTidal10 / a_4; - double A10_1 = -G_AU_Msol_yr * p_M2 / a_2; - double A10_2 = A10_1 * A10_1; - double k10Equilibrium = (3.0 / 2.0) * (16.0 * M_PI / 9.0) * G_AU_Msol_yr * Dnu10 / A10_2 / rOut_3 / omega_t_10; + double k10Equilibrium = k2_prefactor * nuTidal10 * omega_t_10; if (std::isnan(k10Equilibrium)) k10Equilibrium = 0.0; if (w10 < 0.0) k10Equilibrium = -std::abs(k10Equilibrium); - // (l=1, m=2), Viscous dissipation, convective envelope + // (l=2, n=1, m=2), Viscous dissipation, convective envelope double omega_t_12 = std::abs(w12); double omega_tOverOmega_c_12 = omega_t_12 / omegaConv; double nuTidal12 = vl_5; @@ -3494,15 +3479,12 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const else if (utils::Compare(omega_tOverOmega_c_12, 0.01) > 0) { nuTidal12 = vlOver2 / std::sqrt(omega_tOverOmega_c_12); } - double Dnu12 = (99.0 / 14.0) * omega_t_12 * omega_t_12 * m2OverM_2 * (rOut_7 - rIn_7) * rhoConv * nuTidal12 / a_4; - double A12_1 = -G_AU_Msol_yr * p_M2 / a_2; - double A12_2 = A12_1 * A12_1; - double k12Equilibrium = (3.0 / 2.0) * (16.0 * M_PI / 9.0) * G_AU_Msol_yr * Dnu12 / A12_2 / rOut_3 / omega_t_12; + double k12Equilibrium = k2_prefactor * nuTidal12 * omega_t_12; if (std::isnan(k12Equilibrium)) k12Equilibrium = 0.0; if (w12 < 0) k12Equilibrium = -std::abs(k12Equilibrium); - // (l=2, m=2), Viscous dissipation, convective envelope + // (l=2, n=2, m=2), Viscous dissipation, convective envelope double omega_t_22 = std::abs(w22); double omega_tOverOmega_c_22 = omega_t_22 / omegaConv; double nuTidal22 = vl_5; @@ -3512,15 +3494,12 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const else if (utils::Compare(omega_tOverOmega_c_22, 0.01) > 0) { nuTidal22 = vlOver2 / std::sqrt(omega_tOverOmega_c_22); } - double Dnu22 = (28.0 / 3.0) * omega_t_22 * omega_t_22 * m2OverM_2 * (rOut_9 - rIn_9) * rhoConv * nuTidal22 / a_6; - double A22_1 = -G_AU_Msol_yr * p_M2 / a_3; - double A22_2 = A22_1 * A22_1; - double k22Equilibrium = (3.0 / 2.0) * (16.0 * M_PI / 15.0) * G_AU_Msol_yr * Dnu22 / A22_2 / rOut_5 / omega_t_22; + double k22Equilibrium = k2_prefactor * nuTidal22 * omega_t_22; if (std::isnan(k22Equilibrium)) k22Equilibrium = 0.0; if (w22 < 0.0) k22Equilibrium = -std::abs(k22Equilibrium); - // (l=3, m=2), Viscous dissipation, convective envelope + // (l=2, n=3, m=2), Viscous dissipation, convective envelope double omega_t_32 = std::abs(w32); double omega_t_over_omega_c_32 = omega_t_32 / omegaConv; double nuTidal32 = vl_5; @@ -3530,23 +3509,20 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const else if (utils::Compare(omega_t_over_omega_c_32, 0.01) > 0) { nuTidal32 = vlOver2 / std::sqrt(omega_t_over_omega_c_32); } - double Dnu32 = (1495.0 / 132.0) * omega_t_32 * omega_t_32 * m2OverM_2 * (rOut_11 - rIn_11) * rhoConv * nuTidal32 / a_8; - double A32_1 = -G_AU_Msol_yr * p_M2 / a_4; - double A32_2 = A32_1 * A32_1; - double k32Equilibrium = (3.0 / 2.0) * (16.0 * M_PI / 21.0) * G_AU_Msol_yr * Dnu32 / A32_2 / rOut_7 / omega_t_32; + double k32Equilibrium = k2_prefactor * nuTidal32 * omega_t_32; if (std::isnan(k32Equilibrium)) k32Equilibrium = 0.0; if (w32 < 0.0) k32Equilibrium = -std::abs(k32Equilibrium); - // return ImKlmEquilibrium + // return ImKnmEquilibrium return std::make_tuple(k10Equilibrium, k12Equilibrium, k22Equilibrium, k32Equilibrium); } /* - * Calculate the (l,m) = [(1,0), (1,2), (2,2), (3,2)] imaginary components of the potential tidal Love number + * Calculate the l=2, (n,m) = [(1,0), (1,2), (2,2), (3,2)] imaginary components of the potential tidal Love number * by combining Equilibrium and Dynamical tidal contributions. * - * DBL_DBL_DBL_DBL CalculateImKlmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) + * DBL_DBL_DBL_DBL CalculateImKnmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) * * @param [IN] p_Omega Orbital angular frequency (1/yr) * @param [IN] p_SemiMajorAxis Semi-major axis of binary (AU) @@ -3554,15 +3530,15 @@ DBL_DBL_DBL_DBL BaseStar::CalculateImKlmEquilibrium(const double p_Omega, const * @return [(1,0), (1,2), (2,2), (3,2)] Imaginary components of the * potential tidal Love number (unitless) */ -DBL_DBL_DBL_DBL BaseStar::CalculateImKlmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { +DBL_DBL_DBL_DBL BaseStar::CalculateImKnmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { double Imk10Dynamical, Imk12Dynamical, Imk22Dynamical, Imk32Dynamical; - std::tie(Imk10Dynamical, Imk12Dynamical, Imk22Dynamical, Imk32Dynamical) = CalculateImKlmDynamical(p_Omega, p_SemiMajorAxis, p_M2); + std::tie(Imk10Dynamical, Imk12Dynamical, Imk22Dynamical, Imk32Dynamical) = CalculateImKnmDynamical(p_Omega, p_SemiMajorAxis, p_M2); double Imk10Equilibrium, Imk12Equilibrium, Imk22Equilibrium, Imk32Equilibrium; - std::tie(Imk10Equilibrium, Imk12Equilibrium, Imk22Equilibrium, Imk32Equilibrium) = CalculateImKlmEquilibrium(p_Omega, p_SemiMajorAxis, p_M2); + std::tie(Imk10Equilibrium, Imk12Equilibrium, Imk22Equilibrium, Imk32Equilibrium) = CalculateImKnmEquilibrium(p_Omega, p_SemiMajorAxis, p_M2); - // return combined ImKlm terms; + // return combined ImKnm terms; return std::make_tuple(Imk10Dynamical + Imk10Equilibrium, Imk12Dynamical + Imk12Equilibrium, Imk22Dynamical + Imk22Equilibrium, Imk32Dynamical + Imk32Equilibrium); } @@ -3684,6 +3660,31 @@ double BaseStar::CalculateRadialExpansionTimescale_Static(const STELLAR_TYPE p_S } +/* + * Calculate the radial expansion timescale in the mass transfer regime + * We do not use CalculateRadialExpansionTimescale(), since in the process of mass transfer the previous radius + * is determined by binary evolution, not nuclear timescale evolution + * + * + * double CalculateRadialExpansionTimescaleDuringMassTransfer() + * + * @return Radial expansion timescale + */ +double BaseStar::CalculateRadialExpansionTimescaleDuringMassTransfer() { + + // We create and age it slightly to determine how the radius will change. + // To be sure the clone does not participate in logging, we set its persistence to EPHEMERAL. + BaseStar *clone = Clone(OBJECT_PERSISTENCE::EPHEMERAL, false); // do not re-initialise the clone + + double timestep = std::max(1000.0 * NUCLEAR_MINIMUM_TIMESTEP, m_Age / 1.0E6); + clone->UpdateAttributesAndAgeOneTimestep(0.0, 0.0, timestep, true, false); + double radiusAfterAging = clone->Radius(); + delete clone; clone = nullptr; // return the memory allocated for the clone + + return timestep * m_Radius / fabs(m_Radius - radiusAfterAging); +} + + /* * Calculate mass change timescale * @@ -3845,22 +3846,28 @@ double BaseStar::DrawRemnantKickMuller(const double p_COCoreMass) const { double BaseStar::DrawRemnantKickMullerMandel(const double p_COCoreMass, const double p_Rand, const double p_RemnantMass) const { - double remnantKick = -1.0; + double remnantKick; double muKick = 0.0; - double rand = p_Rand; // makes it possible to adjust if p_Rand is too low, to avoid getting stuck + double sigmaKick = 0.0; if (utils::Compare(p_RemnantMass, OPTIONS->MaximumNeutronStarMass()) < 0) { muKick = max(OPTIONS->MullerMandelKickMultiplierNS() * (p_COCoreMass - p_RemnantMass) / p_RemnantMass, 0.0); + sigmaKick = OPTIONS->MullerMandelSigmaKickNS(); } else { muKick = max(OPTIONS->MullerMandelKickMultiplierBH() * (p_COCoreMass - p_RemnantMass) / p_RemnantMass, 0.0); + sigmaKick = OPTIONS->MullerMandelSigmaKickBH(); } - while (remnantKick < 0.0) { - remnantKick = muKick * (1.0 + gsl_cdf_gaussian_Pinv(rand, OPTIONS->MullerMandelSigmaKick())); - rand = min(rand + p_Rand + 0.0001, 1.0); - } - + double quantile0 = gsl_cdf_gaussian_P(-1.0, sigmaKick); //quantile of -1 in the Gaussian CDF; the goal is to draw from the cut-off Gaussian since the kick must exceed 0 + double rand = quantile0 + p_Rand * (1.0 - quantile0); + remnantKick = muKick * (1.0 + gsl_cdf_gaussian_Pinv(rand, sigmaKick)); + + // Mandel * Mueller 2020 call for USSN kicks to be treated in the same way as CCSN kicks; however, if this override flag is set, set the USSN kick to be equal to the user-provided magnitude + if (utils::SNEventType(m_SupernovaDetails.events.current) == SN_EVENT::USSN && OPTIONS->USSNKicksOverrideMandelMuller() ) { + remnantKick = OPTIONS->KickMagnitudeDistributionSigmaForUSSN(); + } + return remnantKick; } @@ -3926,6 +3933,20 @@ double BaseStar::DrawSNKickMagnitude(const double p_Sigma, kickMagnitude = DrawRemnantKickMullerMandel(p_COCoreMass, p_Rand, p_RemnantMass); break; + case KICK_MAGNITUDE_DISTRIBUTION::LOGNORMAL: { // LOGNORMAL + // Only draw Disberg & Mandel (2025) kicks for CCSN or PPISN (if they receive a kick) + // use Maxwellians with sigma set in CalculateSNKickMagnitude() for other SN types + SN_EVENT thisSNevent = utils::SNEventType(m_SupernovaDetails.events.current); // current SN event + if (thisSNevent == SN_EVENT::CCSN || (thisSNevent == SN_EVENT::PPISN && OPTIONS->NatalKickForPPISN())) { + // maximum kick of DISBERG_MANDEL_MAX_KICK = 1000 km/s, following Disberg & Mandel (2025) + // normalise the draw so that the kick is uniformly drawn from the inverse CDF below this maximum + double cdfMax = gsl_cdf_lognormal_P(DISBERG_MANDEL_MAX_KICK, DISBERG_MANDEL_MU, DISBERG_MANDEL_SIGMA); + kickMagnitude = gsl_cdf_lognormal_Pinv(p_Rand*cdfMax, DISBERG_MANDEL_MU, DISBERG_MANDEL_SIGMA); + } + else + kickMagnitude = DrawKickMagnitudeDistributionMaxwell(p_Sigma, p_Rand); + } break; + default: // unknown prescription // the only way this can happen is if someone added a KICK_MAGNITUDE_DISTRIBUTION // and it isn't accounted for in this code. We should not default here, with or without a warning. @@ -4118,7 +4139,7 @@ double BaseStar::CalculateBindingEnergy(const double p_CoreMass, const double p_ * Calculate convective envelope binding energy for the two-stage Hirai & Mandel (2022) common envelope formalism * * - * double CalculateConvectiveEnvelopeBindingEnergy(const double p_TotalMass, const double p_ConvectiveEnvelopeMass, const double p_Radius, const double p_Lambda) + * double CalculateConvectiveEnvelopeBindingEnergy(const double p_TotalMass, const double p_ConvectiveEnvelopeMass, const double p_Radius, const double p_Lambda) const * * @param [IN] p_TotalMass Total mass of the star (Msol) * @param [IN] p_ConvectiveEnvelopeMass Mass of the convective outer envelope (Msol) @@ -4126,7 +4147,7 @@ double BaseStar::CalculateBindingEnergy(const double p_CoreMass, const double p_ * @param [IN] p_Lambda Lambda parameter for the convective envelope * @return Binding energy (erg) */ -double BaseStar::CalculateConvectiveEnvelopeBindingEnergy(const double p_TotalMass, const double p_ConvectiveEnvelopeMass, const double p_Radius, const double p_Lambda) { +double BaseStar::CalculateConvectiveEnvelopeBindingEnergy(const double p_TotalMass, const double p_ConvectiveEnvelopeMass, const double p_Radius, const double p_Lambda) const { return CalculateBindingEnergy(p_TotalMass - p_ConvectiveEnvelopeMass, p_ConvectiveEnvelopeMass, p_Radius, p_Lambda); } @@ -4138,18 +4159,19 @@ double BaseStar::CalculateConvectiveEnvelopeBindingEnergy(const double p_TotalMa * Follows the fits of Picker, Hirai, Mandel (2024), arXiv:2402.13180 for lambda_He * * - * double BaseStar::CalculateConvectiveEnvelopeLambdaPicker(const double p_convectiveEnvelopeMass, const double p_maxConvectiveEnvelopeMass) const + * double BaseStar::CalculateConvectiveEnvelopeLambdaPicker(const DBL_DBL p_convectiveEnvelopeMass) const * - * @param [IN] p_convectiveEnvelopeMass Mass of the outer convective envelope shell - * @param [IN] p_maxConvectiveEnvelopeMass Maximum mass of the outer convective envelope shell at the onset of carbon burning + * @param [IN] p_convectiveEnvelopeMass Tuple: Mass of the outer convective envelope shell and Maximum mass of the outer convective envelope shell at the onset of carbon burning * @return Lambda binding energy parameter for the outer convective envelope */ -double BaseStar::CalculateConvectiveEnvelopeLambdaPicker(const double p_convectiveEnvelopeMass, const double p_maxConvectiveEnvelopeMass) const { +double BaseStar::CalculateConvectiveEnvelopeLambdaPicker(const DBL_DBL p_convectiveEnvelopeMass) const { + double envMass, envMassMax; + std::tie(envMass, envMassMax) = p_convectiveEnvelopeMass; double m2 = 0.0023 * m_Log10Metallicity * m_Log10Metallicity + 0.0088 * m_Log10Metallicity + 0.013; // Eq. (12) and Table 1 of Picker, Hirai, Mandel (2024) double b1 = m2 * m_Mass - 0.23; // Eq. (11) of Picker+ (2024) - double logLambda = p_convectiveEnvelopeMass / p_maxConvectiveEnvelopeMass > 0.3 - ? 0.42 * p_convectiveEnvelopeMass / p_maxConvectiveEnvelopeMass + b1 + double logLambda = envMass / envMassMax > 0.3 + ? 0.42 * envMass / envMassMax + b1 : 0.3 * 0.42 + b1; return exp(logLambda); diff --git a/src/BaseStar.h b/src/BaseStar.h index e0637cb93..233282e8e 100644 --- a/src/BaseStar.h +++ b/src/BaseStar.h @@ -106,10 +106,11 @@ class BaseStar { double HydrogenAbundanceSurface() const { return m_HydrogenAbundanceSurface; } double InitialHeliumAbundance() const { return m_InitialHeliumAbundance; } double InitialHydrogenAbundance() const { return m_InitialHydrogenAbundance; } + double InitialMainSequenceCoreMass() const { return m_InitialMainSequenceCoreMass; } bool IsAIC() const { return (m_SupernovaDetails.events.current & SN_EVENT::AIC) == SN_EVENT::AIC; } bool IsCCSN() const { return (m_SupernovaDetails.events.current & SN_EVENT::CCSN) == SN_EVENT::CCSN; } bool IsHeSD() const { return (m_SupernovaDetails.events.current & SN_EVENT::HeSD) == SN_EVENT::HeSD; } - virtual bool IsDegenerate() const { return false; } // default is not degenerate - White Dwarfs, NS and BH are degenerate + virtual bool IsDegenerate() const { return false; } // default is not degenerate - White Dwarfs, NS and BH are degenerate bool IsECSN() const { return (m_SupernovaDetails.events.current & SN_EVENT::ECSN) == SN_EVENT::ECSN; } bool IsSN_NONE() const { return m_SupernovaDetails.events.current == SN_EVENT::NONE; } bool IsOneOf(const STELLAR_TYPE_LIST p_List) const; @@ -118,9 +119,11 @@ class BaseStar { bool IsSNIA() const { return (m_SupernovaDetails.events.current & SN_EVENT::SNIA) == SN_EVENT::SNIA; } bool IsUSSN() const { return (m_SupernovaDetails.events.current & SN_EVENT::USSN) == SN_EVENT::USSN; } bool LBV_PhaseFlag() const { return m_LBVphaseFlag; } - double LogMetallicityRho() const { return LogMetallicityXi() + 1.0; } // rho in Hurley+ 2000 - double LogMetallicitySigma() const { return m_Log10Metallicity; } // sigma in Hurley+ 2000 - double LogMetallicityXi() const { return m_Log10Metallicity - LOG10_ZSOL; } // xi in Hurley+ 2000 + double LogMetallicityRho() const { return LogMetallicityXiHurley() + 1.0; } // rho in Hurley+ 2000 + double LogMetallicitySigma() const { return m_Log10Metallicity; } // sigma in Hurley+ 2000 + double LogMetallicityXiHurley() const { return m_Log10Metallicity - LOG10_ZSOL_HURLEY; } // xi in Hurley+ 2000 + double LogMetallicityXiAnders() const { return m_Log10Metallicity - LOG10_ZSOL_ANDERS; } // log10(Z / ZSOL_ANDERS) + double LogMetallicityXiAsplund() const { return m_Log10Metallicity - LOG10_ZSOL_ASPLUND; } // log10(Z / ZSOL_ASPLUND) double Luminosity() const { return m_Luminosity; } double MainSequenceCoreMass() const { return m_MainSequenceCoreMass; } double Mass() const { return m_Mass; } @@ -174,6 +177,9 @@ class BaseStar { double Timescale(TIMESCALE p_Timescale) const { return m_Timescales[static_cast(p_Timescale)]; } double TotalMassLossRate() const { return m_TotalMassLossRate; } double TZAMS() const { return m_TZAMS; } + double VelocityX() const { return m_ComponentVelocity.xValue(); } + double VelocityY() const { return m_ComponentVelocity.yValue(); } + double VelocityZ() const { return m_ComponentVelocity.zValue(); } virtual ACCRETION_REGIME WhiteDwarfAccretionRegime() const { return ACCRETION_REGIME::ZERO; } double XExponent() const { return m_XExponent; } @@ -200,23 +206,23 @@ class BaseStar { // member functions - alphabetically - void ApplyMassTransferRejuvenationFactor() { m_Age *= CalculateMassTransferRejuvenationFactor(); } // Apply age rejuvenation factor + void ApplyMassTransferRejuvenationFactor() { m_Age *= CalculateMassTransferRejuvenationFactor(); } // Apply age rejuvenation factor double CalculateBindingEnergy(const double p_CoreMass, const double p_EnvMass, const double p_Radius, const double p_Lambda) const; virtual double CalculateConvectiveCoreMass() const {return 0.0;} virtual double CalculateConvectiveCoreRadius() const {return 0.0;} - double CalculateConvectiveEnvelopeBindingEnergy(const double p_TotalMass, const double p_ConvectiveEnvelopeMass, const double p_Radius, const double p_Lambda); + double CalculateConvectiveEnvelopeBindingEnergy(const double p_TotalMass, const double p_ConvectiveEnvelopeMass, const double p_Radius, const double p_Lambda) const; - double CalculateConvectiveEnvelopeLambdaPicker(const double p_convectiveEnvelopeMass, const double p_maxConvectiveEnvelopeMass) const; + double CalculateConvectiveEnvelopeLambdaPicker(const DBL_DBL p_ConvectiveEnvelopeMass) const; virtual DBL_DBL CalculateConvectiveEnvelopeMass() const { return std::tuple (0.0, 0.0); } virtual double CalculateCriticalMassRatio(const bool p_AccretorIsDegenerate, const double p_massTransferEfficiencyBeta); virtual double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const { return 0.0; } // Default is 0.0 virtual double CalculateCriticalMassRatioGeEtAl(const QCRIT_PRESCRIPTION p_qCritPrescription, - const double p_massTransferEfficiencyBeta) { return InterpolateGeEtAlQCrit(p_qCritPrescription, p_massTransferEfficiencyBeta); } + const double p_massTransferEfficiencyBeta) { return InterpolateGeEtAlQCrit(p_qCritPrescription, p_massTransferEfficiencyBeta); } virtual double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 0.0; } // Default is 0.0 double CalculateDynamicalTimescale() const { return CalculateDynamicalTimescale_Static(m_Mass, m_Radius); } // Use class member variables @@ -226,20 +232,20 @@ class BaseStar { double CalculateEddyTurnoverTimescale() const; virtual void CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams) { } // Default is NO-OP - virtual void CalculateGBParams() { CalculateGBParams(m_Mass0, m_GBParams); } // Use class member variables + virtual void CalculateGBParams() { CalculateGBParams(m_Mass0, m_GBParams); } // Use class member variables - double CalculateInitialHeliumAbundance() const { return 0.24 + 2.0 * m_Metallicity; } // Pols et al. 1998 - double CalculateInitialHydrogenAbundance() const { return 0.76 - 3.0 * m_Metallicity; } // Pols et al. 1998 + double CalculateInitialHeliumAbundance() const { return 0.24 + 2.0 * m_Metallicity; } // Pols et al. 1998 + double CalculateInitialHydrogenAbundance() const { return 0.76 - 3.0 * m_Metallicity; } // Pols et al. 1998 - virtual DBL_DBL_DBL_DBL CalculateImKlmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const; - virtual DBL_DBL_DBL_DBL CalculateImKlmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const ; - virtual DBL_DBL_DBL_DBL CalculateImKlmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const; + virtual DBL_DBL_DBL_DBL CalculateImKnmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const; + virtual DBL_DBL_DBL_DBL CalculateImKnmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const ; + virtual DBL_DBL_DBL_DBL CalculateImKnmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const; - virtual double CalculateLambdaDewi() const { return 1.0; } // Default for stellar types with no LamdaDewi definitions - 1.0 is benign + virtual double CalculateLambdaDewi() const { return 1.0; } // Default for stellar types with no LamdaDewi definitions - 1.0 is benign double CalculateLambdaKruckow(const double p_Radius, const double p_Alpha) const; - double CalculateLambdaKruckow() const { return CalculateLambdaKruckow(m_Radius, OPTIONS->CommonEnvelopeSlopeKruckow()); } - virtual double CalculateLambdaLoveridge(const double p_EnvMass, const bool p_IsMassLoss = false) const { return 1.0; } // Default for non giant branch stars - 1.0 is benign - double CalculateLambdaLoveridge() const { return CalculateLambdaLoveridge(m_Mass - m_CoreMass, false); } + double CalculateLambdaKruckow() const { return CalculateLambdaKruckow(m_Radius, OPTIONS->CommonEnvelopeSlopeKruckow()); } + virtual double CalculateLambdaLoveridge(const double p_EnvMass, const bool p_IsMassLoss = false) const { return 1.0; } // Default for non giant branch stars - 1.0 is benign + double CalculateLambdaLoveridge() const { return CalculateLambdaLoveridge(m_Mass - m_CoreMass, false); } double CalculateLambdaNanjing() const; DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, @@ -260,7 +266,7 @@ class BaseStar { virtual double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // Defaults to MS. k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 virtual double CalculateMomentOfInertiaAU() const { return CalculateMomentOfInertia() * RSOL_TO_AU * RSOL_TO_AU; } - double CalculateNuclearMassLossRate(); + double CalculateNuclearMassLossRate() { return m_Mass / CalculateRadialExpansionTimescaleDuringMassTransfer(); } double CalculateOmegaCHE(const double p_MZAMS, const double p_Metallicity) const; @@ -270,6 +276,8 @@ class BaseStar { double CalculateRadialExpansionTimescale() const { return CalculateRadialExpansionTimescale_Static(m_StellarType, m_StellarTypePrev, m_Radius, m_RadiusPrev, m_DtPrev); } // Use class member variables + double CalculateRadialExpansionTimescaleDuringMassTransfer(); + virtual double CalculateRadialExtentConvectiveEnvelope() const { return 0.0; } // Default for stars with no convective envelope virtual double CalculateRadiusOnMassChange(double p_dM) { return Radius(); } // NO-OP @@ -294,13 +302,12 @@ class BaseStar { double CalculateZetaAdiabatic(); virtual double CalculateZetaConstantsByEnvelope(ZETA_PRESCRIPTION p_ZetaPrescription) { return 0.0; } // Use inheritance hierarchy - double CalculateZetaEquilibrium() { return 0.0; } + virtual double CalculateZetaEquilibrium() { return 0.0; } void ClearCurrentSNEvent() { m_SupernovaDetails.events.current = SN_EVENT::NONE; } // Clear supernova event/state for current timestep - void ClearSupernovaStash() { LOGGING->ClearSSESupernovaStash(); } // Clear contents of SSE supernova stash - virtual ACCRETION_REGIME DetermineAccretionRegime(const bool p_HeRich, - const double p_DonorThermalMassLossRate) { return ACCRETION_REGIME::ZERO; } // Placeholder, use inheritance for WDs + virtual ACCRETION_REGIME DetermineAccretionRegime(const double p_DonorThermalMassLossRate, + const bool p_HeRich) { return ACCRETION_REGIME::ZERO; } // Placeholder, use inheritance for WDs virtual ENVELOPE DetermineEnvelopeType() const { return ENVELOPE::REMNANT; } // Default is REMNANT - but should never be called virtual MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::OTHER; } // Not A, B, C, or NONE @@ -330,13 +337,10 @@ class BaseStar { void SetStellarTypePrev(const STELLAR_TYPE p_StellarTypePrev) { m_StellarTypePrev = p_StellarTypePrev; } - bool ShouldEnvelopeBeExpelledByPulsations() const { return false; } // Default is that there is no envelope expulsion by pulsations + virtual bool ShouldEnvelopeBeExpelledByPulsations() const { return false; } // Default is that there is no envelope expulsion by pulsations virtual void SpinDownIsolatedPulsar(const double p_Stepsize) { } // Default is NO-OP - void StashSupernovaDetails(const STELLAR_TYPE p_StellarType, - const SSE_SN_RECORD_TYPE p_RecordType = SSE_SN_RECORD_TYPE::DEFAULT) { LOGGING->StashSSESupernovaDetails(this, p_StellarType, p_RecordType); } - virtual double TAMSCoreMass() const { return 0.0; } // Except MS stars virtual void UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { } // Default is NO-OP @@ -365,7 +369,7 @@ class BaseStar { return OPTIONS->DetailedOutput() ? LOGGING->LogSSEDetailedOutput(this, p_Id, p_RecordType) : true; // Write record to SSE Detailed Output log file } - bool PrintPulsarEvolutionParameters(const SSE_PULSAR_RECORD_TYPE p_RecordType = SSE_PULSAR_RECORD_TYPE::DEFAULT) const { + bool PrintPulsarEvolutionParameters(const SSE_PULSAR_RECORD_TYPE p_RecordType) const { return OPTIONS->EvolvePulsars() ? LOGGING->LogSSEPulsarEvolutionParameters(this, p_RecordType) : true; } @@ -373,14 +377,14 @@ class BaseStar { return LOGGING->LogSSESupernovaDetails(this, p_RecordType); // Write record to SSE Supernovae log file } - bool PrintStashedSupernovaDetails() { - return LOGGING->LogStashedSSESupernovaDetails(this); // Write record to SSE Supernovae log file - } - bool PrintSwitchLog() const { return OPTIONS->SwitchLog() ? (LOGGING->ObjectSwitchingPersistence() == OBJECT_PERSISTENCE::PERMANENT ? LOGGING->LogSSESwitchLog(this) : true) : true; // Write record to SSE Switchlog log file } + bool PrintSystemSnapshotLog(const SSE_SYSTEM_SNAPSHOT_RECORD_TYPE p_RecordType = SSE_SYSTEM_SNAPSHOT_RECORD_TYPE::DEFAULT) const { + return LOGGING->LogSSESystemSnapshotLog(this, p_RecordType); // Write record to SSE System Parameters file + } + bool PrintSystemParameters(const SSE_SYSPARMS_RECORD_TYPE p_RecordType = SSE_SYSPARMS_RECORD_TYPE::DEFAULT) const { return LOGGING->LogSSESystemParameters(this, p_RecordType); // Write record to SSE System Parameters file } @@ -406,6 +410,7 @@ class BaseStar { // Zero Age Main Sequence double m_InitialHeliumAbundance; // Initial helium abundance (Y) double m_InitialHydrogenAbundance; // Initial hydrogen abundance (X) + double m_InitialMainSequenceCoreMass; // Initial main sequence core mass (used in BRCEK core mass prescription) double m_LZAMS; // ZAMS Luminosity double m_MZAMS; // ZAMS Mass double m_OmegaZAMS; // ZAMS Angular Frequency @@ -523,7 +528,9 @@ class BaseStar { virtual double CalculateCOCoreMassAtPhaseEnd() const { return m_COCoreMass; } // Default is NO-OP virtual double CalculateCOCoreMassOnPhase() const { return m_COCoreMass; } // Default is NO-OP - + double CalculateConvectiveEnvelopeBindingEnergy(const double p_Lambda) const { double convectiveEnvMass, ignore; + std::tie(convectiveEnvMass, ignore) = CalculateConvectiveEnvelopeMass(); + return CalculateConvectiveEnvelopeBindingEnergy(m_Mass, convectiveEnvMass, m_Radius, p_Lambda); } virtual double CalculateCoreMassAtPhaseEnd() const { return m_CoreMass; } // Default is NO-OP static double CalculateCoreMassGivenLuminosity_Static(const double p_Luminosity, const DBL_VECTOR &p_GBParams); virtual double CalculateCoreMassOnPhase() const { return m_CoreMass; } // Default is NO-OP @@ -601,7 +608,7 @@ class BaseStar { double CalculateMassLossRateVMSSabhahit2023(); double CalculateMassLossRateVMSVink2011() const; virtual double CalculateMassLossRateBelczynski2010(); - virtual double CalculateMassLossRateMerritt2024(); + virtual double CalculateMassLossRateMerritt2025(); double CalculateMassLossRateWolfRayetZDependent(const double p_Mu) const; double CalculateMassLossRateWolfRayet(const double p_Mu) const; double CalculateMassLossRateWolfRayetSanderVink2020(const double p_Mu) const; @@ -618,8 +625,7 @@ class BaseStar { static double CalculateOpacity_Static(const double p_HeliumAbundanceSurface); static double CalculateOStarRotationalVelocityAnalyticCDF_Static(const double p_Ve); - static double CalculateOStarRotationalVelocityAnalyticCDFInverse_Static(double p_Ve, void *p_Params); - static double CalculateOStarRotationalVelocity_Static(const double p_Xmin, const double p_Xmax); + double CalculateOStarRotationalVelocity(); double CalculatePerturbationB(const double p_Mass) const; double CalculatePerturbationC(double p_Mass) const; @@ -684,7 +690,7 @@ class BaseStar { const double p_Rand, const double p_EjectaMass, const double p_RemnantMass); - + virtual void EvolveOneTimestepPreamble() { }; // Default is NO-OP STELLAR_TYPE EvolveOnPhase(const double p_DeltaTime); @@ -733,6 +739,34 @@ class BaseStar { void UpdateAttributesAndAgeOneTimestepPreamble(const double p_DeltaMass, const double p_DeltaMass0, const double p_DeltaTime); + + /* + * Functor for CalculateOStarRotationalVelocity() + * + * + * Constructor: initialise the class + * template OStarRotationVelocityFunctor(double p_CDF, ERROR *p_Error) + * + * @param [IN] p_CDF Desired CDF value + * + * Function: calculate the CDF of the O star rotational velocity and compare to desired value + * T OStarRotationVelocityFunctor(double const& p_Ve) + * + * @param [IN] p_Ve Rotational velocity, km s^-1 + * @return Difference between star's Roche Lobe radius and radius after mass loss + */ + template + struct OStarRotationVelocityFunctor { + OStarRotationVelocityFunctor(double p_CDF) { + m_CDF = p_CDF; + } + T operator()(double const& p_Ve) { + + return (CalculateOStarRotationalVelocityAnalyticCDF_Static(p_Ve) - m_CDF); + } + private: + double m_CDF; + }; }; #endif // __BaseStar_h__ diff --git a/src/BinaryConstituentStar.cpp b/src/BinaryConstituentStar.cpp index 4c2ec16f8..3c5452cae 100644 --- a/src/BinaryConstituentStar.cpp +++ b/src/BinaryConstituentStar.cpp @@ -79,7 +79,6 @@ COMPAS_VARIABLE BinaryConstituentStar::StellarPropertyValue(const T_ANY_PROPERTY case ANY_STAR_PROPERTY::RADIAL_EXPANSION_TIMESCALE_POST_COMMON_ENVELOPE: value = RadialExpansionTimescalePostCEE(); break; case ANY_STAR_PROPERTY::RADIAL_EXPANSION_TIMESCALE_PRE_COMMON_ENVELOPE: value = RadialExpansionTimescalePreCEE(); break; case ANY_STAR_PROPERTY::RECYCLED_NEUTRON_STAR: value = ExperiencedRecycledNS(); break; - case ANY_STAR_PROPERTY::RLOF_ONTO_NS: value = ExperiencedRLOFOntoNS(); break; case ANY_STAR_PROPERTY::TEMPERATURE_POST_COMMON_ENVELOPE: value = TemperaturePostCEE() * TSOL; break; case ANY_STAR_PROPERTY::TEMPERATURE_PRE_COMMON_ENVELOPE: value = TemperaturePreCEE() * TSOL; break; case ANY_STAR_PROPERTY::THERMAL_TIMESCALE_POST_COMMON_ENVELOPE: value = ThermalTimescalePostCEE(); break; @@ -223,10 +222,10 @@ void BinaryConstituentStar::CalculateCommonEnvelopeValues() { std::tie(m_CEDetails.convectiveEnvelopeMass, maxConvectiveEnvelopeMass) = CalculateConvectiveEnvelopeMass(); m_CEDetails.radiativeIntershellMass = Mass() - CoreMass() - m_CEDetails.convectiveEnvelopeMass; - if (OPTIONS->CommonEnvelopeFormalism() == CE_FORMALISM::TWO_STAGE) - m_CEDetails.lambda = CalculateConvectiveEnvelopeLambdaPicker(m_CEDetails.convectiveEnvelopeMass, maxConvectiveEnvelopeMass); - - m_CEDetails.convectiveEnvelopeBindingEnergy = CalculateConvectiveEnvelopeBindingEnergy(Mass(), m_CEDetails.convectiveEnvelopeMass, Radius(), m_CEDetails.lambda); + if (OPTIONS->CommonEnvelopeFormalism() == CE_FORMALISM::TWO_STAGE) { + m_CEDetails.lambda = CalculateConvectiveEnvelopeLambdaPicker(std::tie(m_CEDetails.convectiveEnvelopeMass, maxConvectiveEnvelopeMass)); + m_CEDetails.bindingEnergy = CalculateConvectiveEnvelopeBindingEnergy(Mass(), m_CEDetails.convectiveEnvelopeMass, Radius(), m_CEDetails.lambda); + } } diff --git a/src/BinaryConstituentStar.h b/src/BinaryConstituentStar.h index ee1d830a3..17710ce99 100644 --- a/src/BinaryConstituentStar.h +++ b/src/BinaryConstituentStar.h @@ -50,8 +50,7 @@ class BinaryConstituentStar: virtual public Star { m_CEDetails.postCEE.radialExpansionTimescale = DEFAULT_INITIAL_DOUBLE_VALUE; m_Flags.recycledNS = false; - m_Flags.rlofOntoNS = false; - + m_MassLossDiff = DEFAULT_INITIAL_DOUBLE_VALUE; m_MassTransferDiff = DEFAULT_INITIAL_DOUBLE_VALUE; @@ -151,7 +150,6 @@ class BinaryConstituentStar: virtual public Star { bool ExperiencedRecycledNS() const { return m_Flags.recycledNS; } bool ExperiencedRLOF() const { return m_RLOFDetails.experiencedRLOF; } - bool ExperiencedRLOFOntoNS() const { return m_Flags.rlofOntoNS; } double HeCoreMassAtCEE() const { return m_CEDetails.HeCoreMass; } @@ -196,8 +194,6 @@ class BinaryConstituentStar: virtual public Star { void ClearRecycledNS() { m_Flags.recycledNS = false; } void SetRecycledNS() { m_Flags.recycledNS = true; } - void ClearRLOFOntoNS() { m_Flags.rlofOntoNS = false; } - void SetRLOFOntoNS() { m_Flags.rlofOntoNS = true; } void CalculateCommonEnvelopeValues(); @@ -248,7 +244,6 @@ class BinaryConstituentStar: virtual public Star { struct FLAGS { // Miscellaneous flags bool recycledNS; // Indicate whether the accretor was a recycled neutron star - bool rlofOntoNS; // Indicates whether the donor donated mass to neutron star through RLOF } m_Flags; double m_MassLossDiff; diff --git a/src/CH.cpp b/src/CH.cpp index 0eacf0679..b9fd47d8b 100755 --- a/src/CH.cpp +++ b/src/CH.cpp @@ -286,36 +286,33 @@ void CH::UpdateAgeAfterMassLoss() { /* - * Calculate the weight for the OB and WR mass loss prescriptions + * CalculateMassLossFractionOB * - * According to the prescription described in Yoon et al. 2006 (and Szecsi et al. 2015) - * Use OB mass loss rates until Y1 = 0.55, WR mass loss rates above Y2 = 0.7, and - * linearly interpolate for Y1 < Ys < Y2 where Ys is the surface helium fraction. - * - * Since we don't have Ys by default in our models, and detailed models show Ys - * rises ~linearly from 0.2 to 1.0 across the main-sequence, here we simply - * use tau = t / tMS as a proxy for Ys. + * @brief + * Calculate the fraction of mass loss attributable to OB mass loss, per Yoon et al. 2006 + * + * The model described in Yoon et al. 2006 (also Szecsi et al. 2015) uses OB mass loss while the + * He surface abundance is below 0.55, WR mass loss when the surface He abundance is above 0.7, + * and linearly interpolate when the He surface abundance is between those limits. * - * CalculateMassLossRateWeightOB(const double p_HeliumAbundanceSurface) + * This function calculates the fraction of mass loss attributable to OB mass loss, based on + * the He surface abundance and the abundance limits described in Yoon et al. 2006. The value + * returned will be 1.0 if 100% of the mass loss is attributable to OB mass lass, 0.0 if 100% of + * the mass loss is attributable to WR mass loss, and in the range (0.0, 1.0) if the mass loss is + * a mix of OB and WR. * - * @param [IN] p_HeliumAbundanceSurface Surface helium abundance Ys * - * @return weight for OB mass loss rate + * double CalculateMassLossFractionOB(const double p_HeAbundanceSurface) const + * + * @param p_HeAbundanceSurface Helium abundance at the surface of the star + * @return Fraction of mass loss attributable to OB mass loss */ -double CH::CalculateMassLossRateWeightOB(const double p_HeliumAbundanceSurface) { - - const double y1 = 0.55; - const double y2 = 0.70; - double Ys = p_HeliumAbundanceSurface; // Get surface helium abundance - double weight = 0.0; +double CH::CalculateMassLossFractionOB(const double p_HeAbundanceSurface) const { + constexpr double limOB = 0.55; // per Yoon et al. 2006 + constexpr double limWR = 0.70; // per Yoon et al. 2006 - // Calculate the weight as a function of Ys according to the prescription from Yoon et al. 2006 - if (Ys < y1) weight = 1.0; - else if (Ys > y2) weight = 0.0; - else weight = (y2 - Ys) / y2 - y1; - - return weight; + return std::min(1.0, std::max (0.0, (limWR - p_HeAbundanceSurface) / (limWR - limOB))); } @@ -337,7 +334,7 @@ double CH::CalculateMassLossRateBelczynski2010() { double Mdot = 0.0; double MdotOB = 0.0; double MdotWR = 0.0; - double weight = 1.0; // Initialised to 1.0 to allow us to use the OB mass loss rate by default + double fractionOB = 1.0; // Initialised to 1.0 to allow us to use the OB mass loss rate by default // Calculate OB mass loss rate according to the Vink et al. formalism MdotOB = BaseStar::CalculateMassLossRateBelczynski2010(); @@ -348,16 +345,16 @@ double CH::CalculateMassLossRateBelczynski2010() { // Calculate WR mass loss rate MdotWR = BaseStar::CalculateMassLossRateWolfRayetZDependent(0.0); - // Calculate weight for combining these into total mass-loss rate - weight = CalculateMassLossRateWeightOB(m_HeliumAbundanceSurface); + // Calculate fraction for combining these into total mass-loss rate + fractionOB = CalculateMassLossFractionOB(m_HeliumAbundanceSurface); } - // Set dominant mass loss rate - m_DominantMassLossRate = weight == 0 ? MASS_LOSS_TYPE::WR : MASS_LOSS_TYPE::OB; + // Finally, combine each of these prescriptions according to the OB wind fraction + Mdot = (fractionOB * MdotOB) + ((1.0 - fractionOB) * MdotWR); - // Finally, combine each of these prescriptions according to the weight - Mdot = (weight * MdotOB) + ((1.0 - weight) * MdotWR); + // Set dominant mass loss rate + m_DominantMassLossRate = (fractionOB * MdotOB) > ((1.0 - fractionOB) * MdotWR) ? MASS_LOSS_TYPE::OB : MASS_LOSS_TYPE::WR; // Enhance mass loss rate due to rotation Mdot *= CalculateMassLossRateEnhancementRotation(); @@ -374,18 +371,18 @@ double CH::CalculateMassLossRateBelczynski2010() { * * Modifications for CH stars * - * double CalculateMassLossRateMerritt2024() + * double CalculateMassLossRateMerritt2025() * * @return Mass loss rate in Msol per year */ -double CH::CalculateMassLossRateMerritt2024() { - +double CH::CalculateMassLossRateMerritt2025() { + // Define variables double Mdot = 0.0; double MdotOB = 0.0; double MdotWR = 0.0; - double weight = 1.0; // Initialised to 1.0 to allow us to use the OB mass loss rate by default - + double fractionOB = 1.0; // Initialised to 1.0 to allow us to use the OB mass loss rate by default + // Calculate OB mass loss rate according to the chosen prescription MdotOB = BaseStar::CalculateMassLossRateOB(OPTIONS->OBMassLossPrescription()); @@ -396,18 +393,18 @@ double CH::CalculateMassLossRateMerritt2024() { // cloning it, so that we can ask it what its mass loss rate would be if it were // a HeMS star HeMS *clone = HeMS::Clone((HeMS&)static_cast(*this), OBJECT_PERSISTENCE::EPHEMERAL, false); // Do not initialise so that we can use same mass, luminosity, radius etc - MdotWR = clone->CalculateMassLossRateMerritt2024(); // Calculate WR mass loss rate + MdotWR = clone->CalculateMassLossRateMerritt2025(); // Calculate WR mass loss rate delete clone; clone = nullptr; // return the memory allocated for the clone // Calculate weight for combining these into total mass-loss rate - weight = CalculateMassLossRateWeightOB(m_HeliumAbundanceSurface); + fractionOB = CalculateMassLossFractionOB(m_HeliumAbundanceSurface); } - // Set dominant mass loss rate - m_DominantMassLossRate = weight == 0 ? MASS_LOSS_TYPE::WR : MASS_LOSS_TYPE::OB; + // Finally, combine each of these prescriptions according to the OB wind fraction + Mdot = (fractionOB * MdotOB) + ((1.0 - fractionOB) * MdotWR); - // Finally, combine each of these prescriptions according to the weight - Mdot = (weight * MdotOB) + ((1.0 - weight) * MdotWR); + // Set dominant mass loss rate + m_DominantMassLossRate = (fractionOB * MdotOB) > ((1.0 - fractionOB) * MdotWR) ? MASS_LOSS_TYPE::OB : MASS_LOSS_TYPE::WR; // Enhance mass loss rate due to rotation Mdot *= CalculateMassLossRateEnhancementRotation(); @@ -423,6 +420,10 @@ STELLAR_TYPE CH::EvolveToNextPhase() { if (m_Age < m_Timescales[static_cast(TIMESCALE::tMS)]) { // evolving off because of age? stellarType = STELLAR_TYPE::MS_GT_07; // no - must have spun down - evolve as MS star now m_CHE = false; // evolved CH->MS + + // if BRCEK core mass calculations enabled, initialise the core mass based on current mass and central helium fraction + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::BRCEK) && (utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0)) + m_MainSequenceCoreMass = MainSequence::CalculateInitialMainSequenceCoreMass(m_Mass, m_HeliumAbundanceCore); } else { // yes stellarType = STELLAR_TYPE::NAKED_HELIUM_STAR_MS; // evolve as HeMS star now diff --git a/src/CH.h b/src/CH.h index 77741f7f7..1e52ec789 100755 --- a/src/CH.h +++ b/src/CH.h @@ -69,12 +69,12 @@ class CH: virtual public BaseStar, public MS_gt_07 { double CalculateLuminosityAtPhaseEnd() const { return CalculateLuminosityAtPhaseEnd(m_Mass0); } // Use class member variables double CalculateLuminosityOnPhase(const double p_Time, const double p_Mass, const double p_LZAMS) const; - double CalculateLuminosityOnPhase() const { return m_Luminosity; } + double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); } // Mass loss rate double CalculateMassLossRateBelczynski2010(); - double CalculateMassLossRateMerritt2024(); - double CalculateMassLossRateWeightOB(const double p_HeliumAbundanceSurface); + double CalculateMassLossRateMerritt2025(); + double CalculateMassLossFractionOB(const double p_HeAbundanceSurface) const; // Radius double CalculateRadiusOnPhase() const { return m_RZAMS; } // Constant from birth @@ -89,6 +89,8 @@ class CH: virtual public BaseStar, public MS_gt_07 { bool ShouldEvolveOnPhase() const { return m_Age < m_Timescales[static_cast(TIMESCALE::tMS)] && (OPTIONS->OptimisticCHE() || Omega() >= m_OmegaCHE); } // Evolve on CHE phase if age in MS timescale and spinning at least as fast as CHE threshold void UpdateAgeAfterMassLoss(); + + void UpdateMainSequenceCoreMass(const double p_Dt, const double p_TotalMassLossRate) { }; // Do not use core mass calculations during CHE phase }; diff --git a/src/CHeB.h b/src/CHeB.h index f9a092631..97f486be2 100755 --- a/src/CHeB.h +++ b/src/CHeB.h @@ -76,7 +76,7 @@ class CHeB: virtual public BaseStar, public FGB { double CalculateCoreMassOnPhase(const double p_Mass, const double p_Tau) const; double CalculateCoreMassOnPhase() const { return CalculateCoreMassOnPhase(m_Mass0, m_Tau); } // Use class member variables - double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 0.33; } // As coded in BSE. Using the inverse owing to how qCrit is defined in COMPAS. See Hurley et al. 2002 sect. 2.6.1 for additional details. + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_MS_GT_07; } double CalculateHeCoreMassAtPhaseEnd() const { return m_CoreMass; } diff --git a/src/COWD.cpp b/src/COWD.cpp index 31b616c42..b923f1cb8 100755 --- a/src/COWD.cpp +++ b/src/COWD.cpp @@ -1,105 +1,5 @@ #include "COWD.h" -/* For COWDs, calculate: - * - * (a) the maximum mass acceptance rate of this star, as the accretor, during mass transfer, and - * (b) the retention efficiency parameter - * - * - * For a given mass transfer rate, this function computes the amount of mass a WD would retain after - * flashes, as given by appendix B of Claeys+ 2014. - * https://ui.adsabs.harvard.edu/abs/2014A%26A...563A..83C/abstract - * - * - * DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, const bool p_IsHeRich) - * - * @param [IN] p_DonorMassRate Mass transfer rate from the donor - * @param [IN] p_IsHeRich Material is He-rich or not - * @return Tuple containing the Maximum Mass Acceptance Rate (Msun/yr) and Retention Efficiency Parameter - */ -DBL_DBL COWD::CalculateMassAcceptanceRate(const double p_DonorMassRate, const bool p_IsHeRich) { - - m_AccretionRegime = DetermineAccretionRegime(p_IsHeRich, p_DonorMassRate); - - double acceptanceRate = 0.0; // acceptance mass rate - default = 0.0 - double fractionAccreted = 0.0; // accretion fraction - default = 0.0 - - acceptanceRate = p_DonorMassRate * CalculateEtaHe(p_DonorMassRate); - if (!p_IsHeRich) acceptanceRate *= CalculateEtaH(p_DonorMassRate); - - fractionAccreted = acceptanceRate / p_DonorMassRate; - - return std::make_tuple(acceptanceRate, fractionAccreted); -} - - -/* - * Determine the WD accretion regime based on the MT rate and whether the donor is He rich. Also, - * initialize He-Shell detonation or Off-center ignition when necessary, by changing the value - * of m_HeShellDetonation or m_OffCenterIgnition (respectively). - * - * The accretion regime is one of the options listed in enum ACCRETION_REGIME (constants.h) - * - * Note that we have merged the different flashes regimes from Piersanti+ 2014 into a single regime. - * - * ACCRETION_REGIME DetermineAccretionRegime(const bool p_HeRich, const double p_DonorMassLossRate) - * - * @param [IN] p_HeRich Whether the accreted material is helium-rich or not - * @param [IN] p_DonorMassLossRate Donor mass loss rate, in units of Msol / Myr - * @return Current WD accretion regime - */ -ACCRETION_REGIME COWD::DetermineAccretionRegime(const bool p_HeRich, const double p_DonorMassLossRate) { - - double logMdot = log10(p_DonorMassLossRate / MYR_TO_YEAR); // logarithm of the accreted mass (M_sun/yr) - ACCRETION_REGIME regime = ACCRETION_REGIME::ZERO; - - if (p_HeRich) { - // The following coefficients in logMassTransfer limits come from table A1 in Piersanti+ 2014. - double logMassTransferCrit = WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_0 + WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_1 * m_Mass; - double logMassTransferStable = WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_0 + WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_1 * m_Mass; // Piersanti+2014 has several Flashes regimes. Here we group them into one. - double logMassTransferDetonation = WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_0 + WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_1 * m_Mass; // critical value for double detonation regime in Piersanti+ 2014 - if (utils::Compare(logMdot, logMassTransferStable) < 0) { - if (utils::Compare(logMdot, logMassTransferDetonation) > 0) { - regime = ACCRETION_REGIME::HELIUM_FLASHES; - } - else { - regime = ACCRETION_REGIME::HELIUM_ACCUMULATION; - if ((utils::Compare(m_Mass, MASS_DOUBLE_DETONATION_CO) >= 0) && (utils::Compare(m_HeShell, WD_HE_SHELL_MCRIT_DETONATION) >= 0)) { - m_HeShellDetonation = true; - } - } - } - else if (utils::Compare(logMdot, logMassTransferCrit) > 0) { - regime = ACCRETION_REGIME::HELIUM_OPT_THICK_WINDS; - } - else { - regime = ACCRETION_REGIME::HELIUM_STABLE_BURNING; - if ((utils::Compare(logMdot, COWD_LOG_MDOT_MIN_OFF_CENTER_IGNITION) > 0) && (utils::Compare(m_Mass, COWD_MASS_MIN_OFF_CENTER_IGNITION) > 0)) { - m_OffCenterIgnition = true; - } - } - } - else { - // The following coefficients in logMassTransfer limits come from quadratic fits to Nomoto+ 2007 results (table 5) in Mass vs log10 Mdot space, to cover the low-mass end. - double m_Mass_2 = m_Mass * m_Mass; - double logMassTransferCrit = WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_0 + WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_1 * m_Mass + WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_2 * m_Mass_2; - double logMassTransferStable = WD_LOG_MT_LIMIT_NOMOTO_STABLE_0 + WD_LOG_MT_LIMIT_NOMOTO_STABLE_1 * m_Mass + WD_LOG_MT_LIMIT_NOMOTO_STABLE_2 * m_Mass_2; - - if (utils::Compare(logMdot, logMassTransferStable) < 0) { - regime = ACCRETION_REGIME::HYDROGEN_FLASHES; - } - else if (utils::Compare(logMdot, logMassTransferCrit) > 0) { - regime = ACCRETION_REGIME::HYDROGEN_OPT_THICK_WINDS; - } - else { - regime = ACCRETION_REGIME::HYDROGEN_STABLE_BURNING; - } - } - - return regime; -} - - /* * Specifies next stage, if the star changes its phase. * diff --git a/src/COWD.h b/src/COWD.h index 4414eaf93..f3aebab0f 100755 --- a/src/COWD.h +++ b/src/COWD.h @@ -43,8 +43,6 @@ class COWD: virtual public BaseStar, public WhiteDwarfs { p_Metallicity, WD_Baryon_Number.at(STELLAR_TYPE::CARBON_OXYGEN_WHITE_DWARF)); } - ACCRETION_REGIME DetermineAccretionRegime(const bool p_HeRich, const double p_DonorThermalMassLossRate); // Get the current accretion regime. Can also change flags related to SN events. - protected: void Initialise() { @@ -69,12 +67,7 @@ class COWD: virtual public BaseStar, public WhiteDwarfs { const double p_Metallicity) const { return CalculateLuminosityOnPhase_Static(p_Mass, p_Time, p_Metallicity); } double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Mass, m_Age, m_Metallicity); } // Use class member variables - DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, - const bool p_IsHeRich); - DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, - const double p_AccretorMassRate, - const bool p_IsHeRich) { return CalculateMassAcceptanceRate(p_DonorMassRate, p_IsHeRich); } // Ignore the input accretion rate for WDs - + STELLAR_TYPE EvolveToNextPhase(); bool IsSupernova() const { return m_HeShellDetonation || IsMassAboveChandrasekhar(); }; bool ShouldEvolveOnPhase() const { return m_OffCenterIgnition ? false : !IsSupernova(); }; // From https://ui.adsabs.harvard.edu/abs/2017MNRAS.472.1593W/abstract around the end of section 3.2. Also, allows SN. diff --git a/src/EAGB.cpp b/src/EAGB.cpp index e4845aa5f..0d4384d9d 100755 --- a/src/EAGB.cpp +++ b/src/EAGB.cpp @@ -1046,7 +1046,7 @@ STELLAR_TYPE EAGB::ResolveEnvelopeLoss(bool p_Force) { m_Age = HeGB::CalculateAgeOnPhase_Static(m_Mass, m_COCoreMass, timescales(tHeMS), m_GBParams); - HeHG::CalculateGBParams_Static(m_Mass0, m_Mass, LogMetallicityXi(), m_MassCutoffs, m_AnCoefficients, m_BnCoefficients, m_GBParams); + HeHG::CalculateGBParams_Static(m_Mass0, m_Mass, LogMetallicityXiHurley(), m_MassCutoffs, m_AnCoefficients, m_BnCoefficients, m_GBParams); m_Luminosity = HeGB::CalculateLuminosityOnPhase_Static(m_COCoreMass, gbParams(B), gbParams(D)); double R1, R2; @@ -1078,14 +1078,12 @@ STELLAR_TYPE EAGB::ResolveEnvelopeLoss(bool p_Force) { */ bool EAGB::ShouldSkipPhase() const { #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function -#define gbParams(x) m_GBParams[static_cast(GBP::x)] // for convenience and readability - undefined at end of function double McCOBAGB = CalculateCOCoreMassOnPhase(timescales(tHeI) + timescales(tHe)); - double McSN = std::max(gbParams(McSN), 1.05 * McCOBAGB); // hack from Hurley fortran code, doesn't seem to be in the paper JR: do we know why? **Ilya** + double McSN = std::max(CalculateCoreMassAtSupernova_Static(MCH, m_GBParams[static_cast(GBP::McBAGB)]), 1.05 * McCOBAGB); // hack from Hurley fortran code, doesn't seem to be in the paper return (utils::Compare(McSN, m_COCoreMass) < 0); // skip phase if core is heavy enough to go supernova -#undef gbParams #undef timescales } @@ -1121,14 +1119,12 @@ bool EAGB::ShouldEvolveOnPhase() const { */ bool EAGB::IsSupernova() const { #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function -#define gbParams(x) m_GBParams[static_cast(GBP::x)] // for convenience and readability - undefined at end of function double McCOBAGB = CalculateCOCoreMassOnPhase(timescales(tHeI) + timescales(tHe)); - double McSN = std::max(gbParams(McSN), 1.05 * McCOBAGB); // hack from Hurley fortran code, doesn't seem to be in the paper JR: do we know why? **Ilya** + double McSN = std::max(CalculateCoreMassAtSupernova_Static(MCH, m_GBParams[static_cast(GBP::McBAGB)]), 1.05 * McCOBAGB); // hack from Hurley fortran code, doesn't seem to be in the paper JR: do we know why? **Ilya** return (utils::Compare(McSN, m_COCoreMass) <= 0); // core is heavy enough to go Supernova -#undef gbParams #undef timescales } diff --git a/src/ErrorCatalog.h b/src/ErrorCatalog.h index 9c90dfd2c..8a28a7c06 100644 --- a/src/ErrorCatalog.h +++ b/src/ErrorCatalog.h @@ -133,6 +133,8 @@ enum class ERROR: int { TOO_MANY_RETRIES, // generic too many retries TOO_MANY_RLOF_ITERATIONS, // too many iterations in RLOF root finder TOO_MANY_RLOF_TRIES, // too many tries in RLOF root finder + TOO_MANY_RV_ITERATIONS, // too many iterations in rotational velocity root finder + TOO_MANY_RV_TRIES, // too many tries in rotational velocity root finder TOO_MANY_TIMESTEPS_IN_TIMESTEPS_FILE, // too many timesteps in timesteps file (exceeds maximum) UNABLE_TO_CREATE_DIRECTORY, // unable to create directory UNABLE_TO_REMOVE_DIRECTORY, // unable to remove directory @@ -166,6 +168,7 @@ enum class ERROR: int { UNKNOWN_KICK_MAGNITUDE_DISTRIBUTION, // unknown kick magnitude distribution UNKNOWN_LOGFILE, // unknown log file UNKNOWN_LBV_MASS_LOSS_PRESCRIPTION, // unknown LBV mass loss prescription + UNKNOWN_MALTSEV_MODE, // unknown maltsev mode UNKNOWN_MT_ACCRETION_EFFICIENCY_PRESCRIPTION, // unknown mass transfer accretion efficiency prescription UNKNOWN_MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION, // unknown mass transfer angular momentum loss prescription UNKNOWN_MASS_LOSS_PRESCRIPTION, // unknown mass loss prescription @@ -309,6 +312,8 @@ const COMPASUnorderedMap> ERROR_CATA { ERROR::TOO_MANY_RETRIES, { ERROR_SCOPE::ALWAYS, "Too many retries" }}, { ERROR::TOO_MANY_RLOF_ITERATIONS, { ERROR_SCOPE::ALWAYS, "Reached maximum number of iterations when fitting star inside Roche Lobe in RLOF" }}, { ERROR::TOO_MANY_RLOF_TRIES, { ERROR_SCOPE::ALWAYS, "Reached maximum number of tries when fitting star inside Roche Lobe in RLOF" }}, + { ERROR::TOO_MANY_RV_ITERATIONS, { ERROR_SCOPE::ALWAYS, "Reached maximum number of tries when inverting the CDF of rotational velocitiesF" }}, + { ERROR::TOO_MANY_RV_TRIES, { ERROR_SCOPE::ALWAYS, "Reached maximum number of tries when inverting the CDF of rotational velocities" }}, { ERROR::TOO_MANY_TIMESTEPS_IN_TIMESTEPS_FILE, { ERROR_SCOPE::ALWAYS, "Number of timesteps in timestpes file exceeds maximum timesteps" }}, { ERROR::UNABLE_TO_CREATE_DIRECTORY, { ERROR_SCOPE::ALWAYS, "Unable to create directory" }}, { ERROR::UNABLE_TO_REMOVE_DIRECTORY, { ERROR_SCOPE::ALWAYS, "Unable to remove directory" }}, @@ -342,6 +347,7 @@ const COMPASUnorderedMap> ERROR_CATA { ERROR::UNKNOWN_KICK_MAGNITUDE_DISTRIBUTION, { ERROR_SCOPE::ALWAYS, "Unknown kick magnitude distribution" }}, { ERROR::UNKNOWN_LBV_MASS_LOSS_PRESCRIPTION, { ERROR_SCOPE::ALWAYS, "Unknown LBV mass loss prescription" }}, { ERROR::UNKNOWN_LOGFILE, { ERROR_SCOPE::ALWAYS, "Unknown log file" }}, + { ERROR::UNKNOWN_MALTSEV_MODE, { ERROR_SCOPE::ALWAYS, "Unknown Maltsev remnant mass mode" }}, { ERROR::UNKNOWN_MT_CASE, { ERROR_SCOPE::ALWAYS, "Unknown mass transfer case" }}, { ERROR::UNKNOWN_MT_ACCRETION_EFFICIENCY_PRESCRIPTION, { ERROR_SCOPE::ALWAYS, "Unknown mass transfer accretion efficiency prescription" }}, { ERROR::UNKNOWN_MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION, { ERROR_SCOPE::ALWAYS, "Unknown mass transfer angular momentum loss prescription" }}, diff --git a/src/FGB.h b/src/FGB.h index 6e3de10d2..da52ca229 100755 --- a/src/FGB.h +++ b/src/FGB.h @@ -73,6 +73,8 @@ class FGB: virtual public BaseStar, public HG { double CalculateTauAtPhaseEnd() const { return m_Tau; } // NO-OP double CalculateTauOnPhase() const; + + double CalculateZetaEquilibrium() { return 0.0; } // At lowest order, giants with a convective envelope have radii that are insensitive to mass loss (but see Hurley+ 2002, Eq. 56 and Hurley+ 2000, Eq. 47) double ChooseTimestep(const double p_Time) const; diff --git a/src/GiantBranch.cpp b/src/GiantBranch.cpp index 5b4dd9314..9d25e1650 100644 --- a/src/GiantBranch.cpp +++ b/src/GiantBranch.cpp @@ -265,7 +265,7 @@ void GiantBranch::CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams) gbParams(AHe) = CalculateHeRateConstant_Static(); gbParams(B) = CalculateCoreMass_Luminosity_B_Static(p_Mass); - gbParams(D) = CalculateCoreMass_Luminosity_D_Static(p_Mass, LogMetallicityXi(), m_MassCutoffs); + gbParams(D) = CalculateCoreMass_Luminosity_D_Static(p_Mass, LogMetallicityXiHurley(), m_MassCutoffs); gbParams(p) = CalculateCoreMass_Luminosity_p_Static(p_Mass, m_MassCutoffs); gbParams(q) = CalculateCoreMass_Luminosity_q_Static(p_Mass, m_MassCutoffs); @@ -276,9 +276,6 @@ void GiantBranch::CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams) gbParams(McBAGB) = CalculateCoreMassAtBAGB(p_Mass); gbParams(McDU) = CalculateCoreMassAt2ndDredgeUp_Static(gbParams(McBAGB)); gbParams(McBGB) = CalculateCoreMassAtBGB(p_Mass, p_GBParams); - - gbParams(McSN) = CalculateCoreMassAtSupernova_Static(gbParams(McBAGB)); - #undef gbParams } @@ -341,8 +338,6 @@ void GiantBranch::CalculateGBParams_Static(const double p_Mass, gbParams(McBAGB) = CalculateCoreMassAtBAGB_Static(p_Mass, p_BnCoefficients); gbParams(McBGB) = CalculateCoreMassAtBGB_Static(p_Mass, p_MassCutoffs, p_AnCoefficients, p_GBParams); - gbParams(McSN) = CalculateCoreMassAtSupernova_Static(gbParams(McBAGB)); - #undef gbParams } @@ -692,17 +687,8 @@ double GiantBranch::CalculateRemnantRadius() const { double GiantBranch::CalculateRadialExtentConvectiveEnvelope() const{ double convectiveEnvelopeMass, convectiveEnvelopeMassMax; std::tie(convectiveEnvelopeMass, convectiveEnvelopeMassMax) = CalculateConvectiveEnvelopeMass(); - if (utils::Compare(convectiveEnvelopeMass, 0.0) <= 0 || utils::Compare(convectiveEnvelopeMassMax, 0.0) <= 0 ) return 0.0; // massless convective envelope has zero radial extent - - double convectiveCoreMass = CalculateConvectiveCoreMass(); - double convectiveCoreRadius = CalculateConvectiveCoreRadius(); - // assume that the final radiative intershell (if any) would have a density that is a geometric mean of the core density and total density - double radiativeIntershellMass = m_Mass - convectiveCoreMass - convectiveEnvelopeMassMax; - double convectiveCoreRadiusCubed = convectiveCoreRadius * convectiveCoreRadius * convectiveCoreRadius; - double radiativeIntershellDensity = 1.0 / (4.0 /3.0 * M_PI) * std::sqrt(convectiveCoreMass / convectiveCoreRadiusCubed * m_Mass / m_Radius / m_Radius / m_Radius); - double outerEdgeRadiativeIntershellCubed = radiativeIntershellMass / (4.0 / 3.0 * M_PI * radiativeIntershellDensity) + convectiveCoreRadiusCubed; - - return std::sqrt(convectiveEnvelopeMass/convectiveEnvelopeMassMax) * (m_Radius - std::cbrt(outerEdgeRadiativeIntershellCubed)); + if (utils::Compare(convectiveEnvelopeMass, 0.0) <= 0 || utils::Compare(convectiveEnvelopeMassMax, 0.0) <= 0 ) return 0.0; // massless convective envelope has zero radial extent + return std::sqrt(convectiveEnvelopeMass/convectiveEnvelopeMassMax) * (m_Radius - CalculateConvectiveCoreRadius()); } @@ -822,16 +808,17 @@ double GiantBranch::CalculateCoreMassAtBGB_Static(const double p_Mass, /* * Calculate the core mass at which the Asymptotic Giant Branch phase is terminated in a SN/loss of envelope * - * Hurley et al. 2000, eq 75 -- but note we use MECS rather than MCH + * Hurley et al. 2000, eq 75 -- but note we may use MECS rather than MCH as the CO core mass threshold * * - * double CalculateCoreMassAtSupernova_Static(const double p_McBAGB) + * double CalculateCoreMassAtSupernova_Static(const double p_Mthreshold, const double p_McBAGB) * + * @param [IN] p_Mthreshold Threshold mass, typically either MECS for stars that may explode in ECSNe or MCH otherwise * @param [IN] p_McBAGB Core mass at the Base of the Asymptotic Giant Branch in Msol * @return Maximum core mass before supernova on the Asymptotic Giant Branch */ -double GiantBranch::CalculateCoreMassAtSupernova_Static(const double p_McBAGB) { - return std::max(MECS, (0.773 * p_McBAGB) - 0.35); +double GiantBranch::CalculateCoreMassAtSupernova_Static(const double p_Mthreshold, const double p_McBAGB) { + return std::max(p_Mthreshold, (0.773 * p_McBAGB) - 0.35); } @@ -1310,74 +1297,113 @@ double GiantBranch::CalculateRemnantMassBySchneider2020(const double p_COCoreMas */ double GiantBranch::CalculateRemnantMassByMaltsev2024(const double p_COCoreMass, const double p_HeCoreMass) { - ST_VECTOR mtHist = MassTransferDonorHistory(); // mass transfer history vector - MT_CASE massTransferCase = MT_CASE::OTHER; - double log10Z = m_Log10Metallicity - LOG10_ZSOL; // log_{10} (Z/Zsol), for convenience - double M1, M2, M3; - - if (utils::Compare(p_COCoreMass, MALTSEV2024_MMIN) < 0) // NS formation regardless of metallicity and MT history - return CalculateRemnantNSMassMullerMandel(p_COCoreMass, p_HeCoreMass); - - if (utils::Compare(p_COCoreMass, MALTSEV2024_MMAX) > 0) // BH formation regardless of metallicity and MT history - return p_HeCoreMass; - - // determine MT history - this will tell us which Schneider MT case prescription should be used - if (mtHist.size() == 0) { // no history of MT - effectively single star - massTransferCase = MT_CASE::NONE; + ST_VECTOR mtHist = MassTransferDonorHistory(); // mass transfer history vector + MT_CASE massTransferCase = MT_CASE::OTHER; + double log10Z = m_Log10Metallicity - LOG10_ZSOL_ASPLUND; // log_{10} (Z/Zsol), for convenience + constexpr double log10_1 = 0; // useful for the limits later + constexpr double log10_1_div_10 = -1; // useful for the limits later + constexpr double log10_1_div_50 = -1.69897; // useful for the limits later + double M1, M2, M3; // Z-dependent boundary values for SN outcomes (see Maltsev+ 2025) + double remnantMass; + + if (utils::Compare(p_COCoreMass, MALTSEV2024_MMIN) < 0) { // NS formation regardless of metallicity and MT history + m_SupernovaDetails.fallbackFraction = 0; + remnantMass = NEUTRON_STAR_MASS; } - else { // star was MT donor at least once - // determine MT_CASE of first MT event - STELLAR_TYPE mostRecentDonorType = mtHist[0]; // stellar type at first MT event (as donor) - BaseStar* newStar = stellarUtils::NewStar(mostRecentDonorType); // create new (empty) star of correct stellar type - massTransferCase = newStar->DetermineMassTransferTypeAsDonor(); // get MT type as donor - delete newStar; newStar = nullptr; // return the memory allocated for the new star + else if (utils::Compare(p_COCoreMass, MALTSEV2024_MMAX) > 0) { // BH formation regardless of metallicity and MT history + m_SupernovaDetails.fallbackFraction = 1; + remnantMass = p_HeCoreMass; } + else { // Determine MT history - this will tell us which Schneider MT case prescription should be used + + double log10Z_bounded; // This is really log10(Z/Zsol), so it is 0 for Z=Zsol, -1 for Z=Zsol/10 and log10(1/50) for ... + switch (OPTIONS->MaltsevMode()) { + + case MALTSEV_MODE::OPTIMISTIC: + log10Z_bounded = log10Z; + break; + case MALTSEV_MODE::BALANCED: + log10Z_bounded = std::min(std::max(log10Z, log10_1_div_50), log10_1); + break; + case MALTSEV_MODE::PESSIMISTIC: + log10Z_bounded = std::min(std::max(log10Z, log10_1_div_10), log10_1); + break; + default: + // Unrecognized MALTSEV_MODE. Only possible if a new one is added but an + // extra flag is not created here. + THROW_ERROR(ERROR::UNKNOWN_MALTSEV_MODE); // throw error + } - // apply the appropriate remnant mass prescription for the chosen MT case - switch (massTransferCase) { // which MT_CASE? - - case MT_CASE::NONE: // no history of MT - case MT_CASE::OTHER: // if MT happens from naked He stars, WDs, etc., assume that the core properties are not affected - M1 = MALTSEV2024_M1S + (MALTSEV2024_M1S - MALTSEV2024_M1SZ01) * log10Z; - M2 = MALTSEV2024_M2S + (MALTSEV2024_M2S - MALTSEV2024_M2SZ01) * log10Z; - M3 = MALTSEV2024_M3S + (MALTSEV2024_M3S - MALTSEV2024_M3SZ01) * log10Z; - break; - - case MT_CASE::A: // case A MT - M1 = MALTSEV2024_M1A + (MALTSEV2024_M1A - MALTSEV2024_M1AZ01) * log10Z; - M2 = MALTSEV2024_M2A + (MALTSEV2024_M2A - MALTSEV2024_M2AZ01) * log10Z; - M3 = MALTSEV2024_M3A + (MALTSEV2024_M3A - MALTSEV2024_M3AZ01) * log10Z; - break; - - case MT_CASE::B: // case B MT - M1 = MALTSEV2024_M1B + (MALTSEV2024_M1B - MALTSEV2024_M1BZ01) * log10Z; - M2 = MALTSEV2024_M2B + (MALTSEV2024_M2B - MALTSEV2024_M2BZ01) * log10Z; - M3 = MALTSEV2024_M3B + (MALTSEV2024_M3B - MALTSEV2024_M3BZ01) * log10Z; - break; - - case MT_CASE::C: // case C MT - M1 = MALTSEV2024_M1C + (MALTSEV2024_M1C - MALTSEV2024_M1CZ01) * log10Z; - M2 = MALTSEV2024_M2C + (MALTSEV2024_M2C - MALTSEV2024_M2CZ01) * log10Z; - M3 = MALTSEV2024_M3C + (MALTSEV2024_M3C - MALTSEV2024_M3CZ01) * log10Z; - break; - - default: // unknown MT_CASE - // the only way this can happen is if someone added an MT_CASE - // and it isn't accounted for in this code. We should not default here, with or without a warning. - // We are here because DetermineMassTransferTypeAsDonor() returned an MT_CASE this code doesn't - // account for, and that should be flagged as an error and result in termination of the evolution - // of the star or binary. - // The correct fix for this is to add code for the missing MT_CASE or, if the missing MT_CASE is - // incorrect/superfluous, remove it from the possible MT_CASE values. + if (mtHist.size() == 0) { // no history of MT - effectively single star + massTransferCase = MT_CASE::NONE; + } + else { // star was MT donor at least once + // determine MT_CASE of first MT event + STELLAR_TYPE stellarTypeAtFirstDonation = mtHist[0]; // stellar type at first MT event (as donor) + BaseStar* newStar = stellarUtils::NewStar(stellarTypeAtFirstDonation); // create new (empty) star of correct stellar type + massTransferCase = newStar->DetermineMassTransferTypeAsDonor(); // get MT type as donor + delete newStar; newStar = nullptr; // return the memory allocated for the new star + } + + // If self-stripped, re-classify as Case B + if (massTransferCase == MT_CASE::NONE && HydrogenAbundanceSurface() == 0.0) massTransferCase = MT_CASE::B; // if a star was stripped by winds, treat it as if it experienced Case B mass transfer + + // apply the appropriate remnant mass prescription for the chosen MT case + switch (massTransferCase) { // which MT_CASE? + + case MT_CASE::NONE: // no history of MT + case MT_CASE::OTHER: // if MT happens from naked He stars, WDs, etc., assume that the core properties are not affected + M1 = MALTSEV2024_M1S + (MALTSEV2024_M1S - MALTSEV2024_M1SZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2S + (MALTSEV2024_M2S - MALTSEV2024_M2SZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3S + (MALTSEV2024_M3S - MALTSEV2024_M3SZ01) * log10Z_bounded; + break; + + case MT_CASE::A: // case A MT + M1 = MALTSEV2024_M1A + (MALTSEV2024_M1A - MALTSEV2024_M1AZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2A + (MALTSEV2024_M2A - MALTSEV2024_M2AZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3A + (MALTSEV2024_M3A - MALTSEV2024_M3AZ01) * log10Z_bounded; + break; + + case MT_CASE::B: // case B MT + M1 = MALTSEV2024_M1B + (MALTSEV2024_M1B - MALTSEV2024_M1BZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2B + (MALTSEV2024_M2B - MALTSEV2024_M2BZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3B + (MALTSEV2024_M3B - MALTSEV2024_M3BZ01) * log10Z_bounded; + break; + + case MT_CASE::C: // case C MT + M1 = MALTSEV2024_M1C + (MALTSEV2024_M1C - MALTSEV2024_M1CZ01) * log10Z_bounded; + M2 = MALTSEV2024_M2C + (MALTSEV2024_M2C - MALTSEV2024_M2CZ01) * log10Z_bounded; + M3 = MALTSEV2024_M3C + (MALTSEV2024_M3C - MALTSEV2024_M3CZ01) * log10Z_bounded; + break; + + default: // unknown MT_CASE + // the only way this can happen is if someone added an MT_CASE + // and it isn't accounted for in this code. We should not default here, with or without a warning. + // We are here because DetermineMassTransferTypeAsDonor() returned an MT_CASE this code doesn't + // account for, and that should be flagged as an error and result in termination of the evolution + // of the star or binary. + // The correct fix for this is to add code for the missing MT_CASE or, if the missing MT_CASE is + // incorrect/superfluous, remove it from the possible MT_CASE values. + + THROW_ERROR(ERROR::UNKNOWN_MT_CASE); // throw error + } + - THROW_ERROR(ERROR::UNKNOWN_MT_CASE); // throw error + if( utils::Compare(p_COCoreMass, M3) >=0 || (utils::Compare(p_COCoreMass, M1) >= 0 && utils::Compare(p_COCoreMass, M2) <= 0) ) { // Complete fallback into BH + m_SupernovaDetails.fallbackFraction = 1; + remnantMass = p_HeCoreMass; + } + else if ( utils::Compare(p_COCoreMass, M2) > 0 && utils::Compare(p_COCoreMass, M3) < 0 && utils::Compare(RAND->Random(0, 1), 0.1) <= 0 ) { // Partial fallback BH formation + // add fallback back on + m_SupernovaDetails.fallbackFraction = OPTIONS->MaltsevFallback(); + remnantMass = (p_HeCoreMass - NEUTRON_STAR_MASS)*m_SupernovaDetails.fallbackFraction + NEUTRON_STAR_MASS; + } + else { + m_SupernovaDetails.fallbackFraction = 0; + remnantMass = NEUTRON_STAR_MASS; + } } - - if( utils::Compare(p_COCoreMass, M3) >=0 || (utils::Compare(p_COCoreMass, M1) >= 0 && utils::Compare(p_COCoreMass, M2) <= 0) ) // Complete fallback into BH - return p_HeCoreMass; - else if ( utils::Compare(p_COCoreMass, M2) > 0 && utils::Compare(p_COCoreMass, M3) < 0 && utils::Compare(RAND->Random(0, 1), 0.1) <= 0 ) // Partial fallback BH formation - return CalculateFallbackBHMassMullerMandel(p_COCoreMass, p_HeCoreMass); - return CalculateRemnantNSMassMullerMandel(p_COCoreMass, p_HeCoreMass); + return remnantMass; } @@ -1927,19 +1953,18 @@ STELLAR_TYPE GiantBranch::ResolveCoreCollapseSN() { case REMNANT_MASS_PRESCRIPTION::SCHNEIDER2020: // Schneider 2020 m_Mass = CalculateRemnantMassBySchneider2020(m_COCoreMass); - m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // Fallback fraction of mass beyond proto-neutron-star for BH formation and kicks + m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // fallback fraction of mass beyond proto-neutron-star for BH formation and kicks break; case REMNANT_MASS_PRESCRIPTION::SCHNEIDER2020ALT: // Schneider 2020, alternative m_Mass = CalculateRemnantMassBySchneider2020Alt(m_COCoreMass); - m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // Fallback fraction of mass beyond proto-neutron-star for BH formation and kicks + m_SupernovaDetails.fallbackFraction = utils::Compare(m_Mass, OPTIONS->MaximumNeutronStarMass() ) > 0 ? (m_Mass - NEUTRON_STAR_MASS) / (mass - NEUTRON_STAR_MASS) : 0.0; // fallback fraction of mass beyond proto-neutron-star for BH formation and kicks break; case REMNANT_MASS_PRESCRIPTION::MALTSEV2024: // Maltsev+ 2024 - m_SupernovaDetails.fallbackFraction = 0.0; // no subsequent kick adjustment by fallback fraction needed; MULLERMANDEL kick prescription should be used - m_Mass = CalculateRemnantMassByMaltsev2024(m_COCoreMass, m_HeCoreMass); + m_Mass = CalculateRemnantMassByMaltsev2024(m_COCoreMass, m_HeCoreMass); // fallback fraction determined internally break; @@ -2248,14 +2273,11 @@ STELLAR_TYPE GiantBranch::ResolveSupernova() { if (!utils::IsOneOf(stellarType, { STELLAR_TYPE::NEUTRON_STAR })) m_SupernovaDetails.rocketKickMagnitude = 0; // only NSs can get rocket kicks - // Stash SN details for later printing to the SSE Supernova log. + // Print SN details to the SSE Supernova log. // Only if SSE (BSE does its own SN printing), and only if not an ephemeral clone - // Can't print it now because we may revert state (in Star::EvolveOneTimestep()). - // Will be printed in Star::EvolveOneTimestep() after timestep is accepted (i.e. we don't revert state). - // Need to record the stellar type to which the star will switch if we don't revert state. - - if (OPTIONS->EvolutionMode() == EVOLUTION_MODE::SSE && m_ObjectPersistence == OBJECT_PERSISTENCE::PERMANENT) - StashSupernovaDetails(stellarType); + if (OPTIONS->EvolutionMode() == EVOLUTION_MODE::SSE && m_ObjectPersistence == OBJECT_PERSISTENCE::PERMANENT) { + PrintSupernovaDetails(); + } } } diff --git a/src/GiantBranch.h b/src/GiantBranch.h index 230c49eb1..e9d8e4de7 100755 --- a/src/GiantBranch.h +++ b/src/GiantBranch.h @@ -36,7 +36,7 @@ class GiantBranch: virtual public BaseStar, public MainSequence { double CalculateCoreMassAtBGB(const double p_Mass, const DBL_VECTOR &p_GBParams); static double CalculateCoreMassAtBGB_Static(const double p_Mass, const DBL_VECTOR &p_MassCutoffs, const DBL_VECTOR &p_AnCoefficients, const DBL_VECTOR &p_GBParams); double CalculateCoreMassAtHeIgnition(const double p_Mass) const; - static double CalculateCoreMassAtSupernova_Static(const double p_McBAGB); + static double CalculateCoreMassAtSupernova_Static(const double p_Mthreshold, const double p_McBAGB); static double CalculateCoreMass_Luminosity_B_Static(const double p_Mass); static double CalculateCoreMass_Luminosity_D_Static(const double p_Mass, const double p_LogMetallicityXi, const DBL_VECTOR &p_MassCutoffs); @@ -120,8 +120,6 @@ class GiantBranch: virtual public BaseStar, public MainSequence { void CalculateTimescales(const double p_Mass, DBL_VECTOR &p_Timescales); void CalculateTimescales() { CalculateTimescales(m_Mass0, m_Timescales); } // Use class member variables - - double CalculateZetaEquilibrium() { return 0.0; } // At lowest order, giants with a convective envelope have radii that are insensitive to mass loss (but see Hurley+ 2002, Eq. 56 and Hurley+ 2000, Eq. 47) double CalculateZetaConstantsByEnvelope(ZETA_PRESCRIPTION p_ZetaPrescription); double CalculateZetaConvectiveEnvelopeGiant(ZETA_PRESCRIPTION p_ZetaPrescription); diff --git a/src/HG.cpp b/src/HG.cpp index 265bfd53b..c0f2c3497 100755 --- a/src/HG.cpp +++ b/src/HG.cpp @@ -134,7 +134,10 @@ double HG::CalculateLambdaLoveridge(const double p_EnvMass, const bool p_IsMassL logBindingEnergy += 33.29866; // + logBE0 double bindingEnergy = PPOW(10.0, logBindingEnergy); - return utils::Compare(bindingEnergy, 0.0) > 0 && utils::Compare(p_EnvMass, 0.0) > 0 ? (G_CGS * m_Mass * MSOL_TO_G * p_EnvMass * MSOL_TO_G) / (m_Radius * RSOL_TO_AU * AU_TO_CM * bindingEnergy) : 1.0; // default to 1.0 (usual lambda default) if binding energy is not sensible [should never happen] or if envelope mass is not positive [can be zero] + double lambda = utils::Compare(bindingEnergy, 0.0) > 0 && utils::Compare(1.0 / bindingEnergy, 0.0) > 0 && utils::Compare(p_EnvMass, MAXIMUM_MASS_LOSS_FRACTION * m_Mass) > 0 + ? (G_CGS * m_Mass * MSOL_TO_G * p_EnvMass * MSOL_TO_G) / (m_Radius * RSOL_TO_AU * AU_TO_CM * bindingEnergy) + : 1.0; // default to 1.0 (usual lambda default) if binding energy is not sensible [sometimes can be infinite if logBindingEnergy is too high] or if envelope mass is too low to reliably evaluate lambda [can be zero] + return utils::Compare(lambda, 0.0) > 0 ? lambda : 1.0; // final check to avoid returning zero lambda } diff --git a/src/HG.h b/src/HG.h index a8bd2f7c2..915872ea3 100755 --- a/src/HG.h +++ b/src/HG.h @@ -81,7 +81,7 @@ class HG: virtual public BaseStar, public GiantBranch { double CalculateCoreMassOnPhaseIgnoringPreviousCoreMass(const double p_Mass, const double p_Time) const; // Ignore previous core mass constraint when computing expected core mass double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const; - double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 0.25; } // As coded in BSE. Using the inverse owing to how qCrit is defined in COMPAS. See Hurley et al. 2002 sect. 2.6.1 for additional details. + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_HG; } double CalculateHeCoreMassAtPhaseEnd() const { return m_CoreMass; } // McHe(HG) = Core Mass double CalculateHeCoreMassOnPhase() const { return m_CoreMass; } // McHe(HG) = Core Mass @@ -90,14 +90,14 @@ class HG: virtual public BaseStar, public GiantBranch { double CalculateHeliumAbundanceCoreOnPhase() const { return 1.0 - m_Metallicity; } // Use class member variables double CalculateHeliumAbundanceSurfaceAtPhaseEnd() const { return CalculateHeliumAbundanceSurfaceOnPhase(); } - double CalculateHeliumAbundanceSurfaceOnPhase() const { return m_InitialHeliumAbundance; } // Use class member variables + double CalculateHeliumAbundanceSurfaceOnPhase() const { return m_HeliumAbundanceSurface; } // Use class member variables double CalculateHydrogenAbundanceCoreAtPhaseEnd() const { return CalculateHydrogenAbundanceCoreOnPhase(); } double CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) const; double CalculateHydrogenAbundanceCoreOnPhase() const { return 0.0; } // Star has exhausted hydrogen in its core double CalculateHydrogenAbundanceSurfaceAtPhaseEnd() const { return CalculateHydrogenAbundanceSurfaceOnPhase(); } - double CalculateHydrogenAbundanceSurfaceOnPhase() const { return m_InitialHydrogenAbundance; } // Use class member variables + double CalculateHydrogenAbundanceSurfaceOnPhase() const { return m_HydrogenAbundanceSurface; } // Use class member variables diff --git a/src/HeGB.h b/src/HeGB.h index 93807db3e..56dc03824 100755 --- a/src/HeGB.h +++ b/src/HeGB.h @@ -59,7 +59,7 @@ class HeGB: virtual public BaseStar, public HeHG { // member functions - alphabetically double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const ; - double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 1.28; } // From BSE. Using the inverse owing to how qCrit is defined in COMPAS. See Hurley et al. 2002 sect. 2.6.1 for additional details. + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_HE_GIANT; } double CalculateLuminosityOnPhase(const double p_CoreMass, const double p_GBPB, const double p_GBPD) const { return CalculateLuminosityOnPhase_Static(p_CoreMass, p_GBPB, p_GBPD); } double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_CoreMass, m_GBParams[static_cast(GBP::B)], m_GBParams[static_cast(GBP::D)]); } diff --git a/src/HeHG.cpp b/src/HeHG.cpp index ef4102ef8..de947ab54 100755 --- a/src/HeHG.cpp +++ b/src/HeHG.cpp @@ -65,8 +65,6 @@ void HeHG::CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams) { gbParams(McBAGB) = CalculateCoreMassAtBAGB(); gbParams(McBGB) = CalculateCoreMassAtBGB(p_Mass, p_GBParams); - gbParams(McSN) = CalculateCoreMassAtSupernova_Static(gbParams(McBAGB)); - #undef gbParams } @@ -134,8 +132,6 @@ void HeHG::CalculateGBParams_Static(const double p_Mass0, gbParams(McBAGB) = p_Mass0; gbParams(McBGB) = GiantBranch::CalculateCoreMassAtBGB_Static(p_Mass, p_MassCutoffs, p_AnCoefficients, p_GBParams); - gbParams(McSN) = CalculateCoreMassAtSupernova_Static(gbParams(McBAGB)); - #undef gbParams } @@ -274,7 +270,7 @@ double HeHG::CalculateRemnantRadius() const { * * This implementation adapted from the STARTRACK implementation (STARTRACK courtesy Chris Belczynski) * - * This function good for HeHG and HeGB stars (for Helium stars: always use Natasha's fit) + * This function is for HeHG and HeGB stars (for Helium stars: always use Natasha Ivanova's fit) * * * double CalculateLambdaNanjingStarTrack(const double p_Mass, const double p_Metallicity) @@ -286,11 +282,11 @@ double HeHG::CalculateRemnantRadius() const { */ double HeHG::CalculateLambdaNanjingStarTrack(const double p_Mass, const double p_Metallicity) const { - double rMin = 0.25; // minimum considered radius: Natasha JR: should this be in constants.h? Maybe not... Who is Natasha? **Ilya** - double rMax = 120.0; // maximum considered radius: Natasha JR: should this be in constants.h? Maybe not... Who is Natasha? **Ilya** + double rMin = 0.25; // minimum considered radius: Natasha + double rMax = 120.0; // maximum considered radius: Natasha - double rMinLambda = 0.3 * PPOW(rMin, -0.8); // JR: todo: should this be in constants.h? JR: should this be in constants.h? Maybe not... **Ilya** - double rMaxLambda = 0.3 * PPOW(rMax, -0.8); // JR: todo: should this be in constants.h? JR: should this be in constants.h? Maybe not... **Ilya** + double rMinLambda = 0.3 * PPOW(rMin, -0.8); + double rMaxLambda = 0.3 * PPOW(rMax, -0.8); return m_Radius < rMin ? rMinLambda : (m_Radius > rMax ? rMaxLambda : 0.3 * PPOW(m_Radius, -0.8)); } @@ -412,12 +408,10 @@ double HeHG::ChooseTimestep(const double p_Time) const { * @return Boolean flag: true if evolution on this phase should continue, false if not */ bool HeHG::ShouldEvolveOnPhase() const { -#define gbParams(x) m_GBParams[static_cast(GBP::x)] // for convenience and readability - undefined at end of function double McMax = CalculateMaximumCoreMass(m_Mass); - return ((utils::Compare(m_COCoreMass, McMax) <= 0 || utils::Compare(McMax, gbParams(McSN)) >= 0) && !ShouldEnvelopeBeExpelledByPulsations()); // Evolve on HeHG phase if McCO <= McMax or McMax >= McSN and envelope is not ejected by pulsations - -#undef gbParams + double McSN = CalculateCoreMassAtSupernova_Static(MECS, m_GBParams[static_cast(GBP::McBAGB)]); + return ((utils::Compare(m_COCoreMass, McMax) <= 0 || utils::Compare(McMax, McSN) >= 0) && !ShouldEnvelopeBeExpelledByPulsations()); // Evolve on HeHG phase if McCO <= McMax or McMax >= McSN and envelope is not ejected by pulsations } @@ -484,7 +478,7 @@ bool HeHG::IsSupernova() const { return (utils::Compare(m_Mass, MECS) > 0); } - return (utils::Compare(m_COCoreMass, CalculateCoreMassAtSupernova_Static(m_GBParams[static_cast(GBP::McBAGB)])) >= 0); // Go supernova if CO core mass large enough + return (utils::Compare(m_COCoreMass, CalculateCoreMassAtSupernova_Static(MECS, m_GBParams[static_cast(GBP::McBAGB)])) >= 0); // Go supernova if CO core mass large enough } /* diff --git a/src/HeHG.h b/src/HeHG.h index bf60c709f..9b45a45d5 100755 --- a/src/HeHG.h +++ b/src/HeHG.h @@ -69,13 +69,19 @@ class HeHG: virtual public BaseStar, public HeMS { double CalculateCoreMassOnPhase() const { return m_COCoreMass; } // Mc(HeMS) = McCOMass double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const; - double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 1.28; } // From BSE. Using the inverse owing to how qCrit is defined in COMPAS. See Hurley et al. 2002 sect. 2.6.1 for additional details. + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_HE_GIANT; } void CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams); void CalculateGBParams() { CalculateGBParams(m_Mass0, m_GBParams); } // Use class member variables double CalculateHeCoreMassAtPhaseEnd() const { return CalculateHeCoreMassOnPhase(); } // Same as on phase double CalculateHeCoreMassOnPhase() const { return m_Mass; } // NO-OP + + double CalculateHeliumAbundanceCoreAtPhaseEnd() const { return 0.0; } + double CalculateHeliumAbundanceCoreOnPhase() const { return 0.0; } + + double CalculateHydrogenAbundanceCoreAtPhaseEnd() const { return 0.0; } + double CalculateHydrogenAbundanceCoreOnPhase() const { return 0.0; } double CalculateLambdaNanjingStarTrack(const double p_Mass, const double p_Metallicity) const; double CalculateLambdaNanjingEnhanced(const int p_MassIndex, const STELLAR_POPULATION p_StellarPop) const { return CalculateLambdaNanjingStarTrack(0.0, 0.0); } // 0.0 are dummy values that are not used diff --git a/src/HeMS.cpp b/src/HeMS.cpp index e68e13263..a7420e3ab 100755 --- a/src/HeMS.cpp +++ b/src/HeMS.cpp @@ -399,11 +399,11 @@ double HeMS::CalculateMassLossRateWolfRayetShenar2019() const { * Calculate the mass loss rate for helium stars in the updated prescription * Uses Sander & Vink 2020 for Wolf--Rayet stars * - * double CalculateMassLossRateMerritt2024() + * double CalculateMassLossRateMerritt2025() * * @return Mass loss rate in Msol per year */ -double HeMS::CalculateMassLossRateMerritt2024() { +double HeMS::CalculateMassLossRateMerritt2025() { double MdotWR = 0.0; diff --git a/src/HeMS.h b/src/HeMS.h index ffc92607c..08a4786f3 100644 --- a/src/HeMS.h +++ b/src/HeMS.h @@ -47,7 +47,7 @@ class HeMS: virtual public BaseStar, public TPAGB { static double CalculateLuminosityAtPhaseEnd_Static(const double p_Mass); double CalculateMassLossRateBelczynski2010(); - double CalculateMassLossRateMerritt2024(); + double CalculateMassLossRateMerritt2025(); static DBL_DBL CalculateRadiusAtPhaseEnd_Static(const double p_Mass, const double p_Luminosity); static double CalculateRadiusAtZAMS_Static(const double p_Mass); @@ -61,6 +61,10 @@ class HeMS: virtual public BaseStar, public TPAGB { protected: void Initialise() { + // initialise surface abundances + m_HydrogenAbundanceSurface = 0.0; + m_HeliumAbundanceSurface = 1.0 - m_Metallicity; + CalculateTimescales(); // JR: Age for HeMS is partially calculated before switching - // can get here from various places in ResolveEnvelopeLoss(), @@ -85,7 +89,7 @@ class HeMS: virtual public BaseStar, public TPAGB { static double CalculateCoreMass_Luminosity_q_Static(const double p_Mass, const DBL_VECTOR &p_MassCutoffs) { return 3.0; } double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const; - double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 0.33; } // As coded in BSE. Using the inverse owing to how qCrit is defined in COMPAS. See Hurley et al. 2002 sect. 2.6.1 for additional details. + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_MS_GT_07; } void CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams); void CalculateGBParams() { CalculateGBParams(m_Mass0, m_GBParams); } // Use class member variables diff --git a/src/HeWD.cpp b/src/HeWD.cpp index ef77e3951..9d8f60423 100755 --- a/src/HeWD.cpp +++ b/src/HeWD.cpp @@ -21,7 +21,7 @@ */ DBL_DBL HeWD::CalculateMassAcceptanceRate(const double p_DonorMassRate, const bool p_IsHeRich) { - m_AccretionRegime = DetermineAccretionRegime(p_IsHeRich, p_DonorMassRate); // Check if accretion leads to stage switch for WDs and returns retention efficiency as well. + m_AccretionRegime = DetermineAccretionRegime(p_DonorMassRate, p_IsHeRich); // Check if accretion leads to stage switch for WDs and returns retention efficiency as well. double acceptanceRate = 0.0; // acceptance mass rate - default = 0.0 double fractionAccreted = m_AccretionRegime == ACCRETION_REGIME::HELIUM_WHITE_DWARF_HYDROGEN_FLASHES ? 0.0 : 1.0; // accretion fraction - default = 1.0, but flashes restrict accumulation @@ -37,19 +37,19 @@ DBL_DBL HeWD::CalculateMassAcceptanceRate(const double p_DonorMassRate, const bo * * The accretion regime is one of the options listed in enum ACCRETION_REGIME (constants.h) * - * ACCRETION_REGIME DetermineAccretionRegime(const bool p_HeRich, const double p_DonorMassLossRate) + * ACCRETION_REGIME DetermineAccretionRegime(const double p_DonorMassLossRate, const bool p_HeRich) * - * @param [IN] p_HeRich Whether the accreted material is helium-rich or not * @param [IN] p_DonorMassRate Donor mass loss rate, in units of Msol / Myr + * @param [IN] p_HeRich Whether the accreted material is helium-rich or not * @return Current WD accretion regime */ -ACCRETION_REGIME HeWD::DetermineAccretionRegime(const bool p_HeRich, const double p_DonorMassRate) { +ACCRETION_REGIME HeWD::DetermineAccretionRegime(const double p_DonorMassRate, const bool p_HeRich) { double Mdot = p_DonorMassRate / MYR_TO_YEAR; // Accreted mass rate (M_sun/yr) ACCRETION_REGIME regime; if (p_HeRich) { if (utils::Compare(Mdot, HEWD_HE_MDOT_CRIT) <= 0) { regime = ACCRETION_REGIME::HELIUM_WHITE_DWARF_HELIUM_SUB_CHANDRASEKHAR; // Could lead to Sub-Chandrasekhar SN Ia - double massSubCh = -4.0e8 * Mdot + 1.34; // Minimum mass for Sub-Chandrasekhar Mass detonation. Eq 62, Belczynski+ 2008. + double massSubCh = WD_BELCZYNSKI_SN_CONSTANT - WD_BELCZYNSKI_SN_LINEAR * Mdot; // Minimum mass for Sub-Chandrasekhar Mass detonation. Eq 62, Belczynski+ 2008. if (utils::Compare(m_Mass, massSubCh) >= 0 ) { m_IsSubChandrasekharTypeIa = true; } @@ -57,8 +57,8 @@ ACCRETION_REGIME HeWD::DetermineAccretionRegime(const bool p_HeRich, const doubl else { regime = ACCRETION_REGIME::HELIUM_WHITE_DWARF_HELIUM_IGNITION; // Could lift degeneracy and evolve into He MS. Requires minimum mass ! on top of the shell size if (utils::Compare(m_Mass, HEWD_MINIMUM_MASS_IGNITION) >= 0) { - if (utils::Compare(Mdot, 1.64e-6) < 0) { // Accretion limit from eq 61, Belczynski+ 2008. - double mCritHeShell = -7.8e-4 * Mdot + 0.13; // Minimum shell mass of He for detonation. Eq 61, Belczynski+ 2008. This helium should not be burnt, but not implemented this yet. Ruiter+ 2014. + if (utils::Compare(Mdot, WD_BELCZYNSKI_IMMEDIATE_FLASH) < 0) { // Accretion limit from eq 61, Belczynski+ 2008. + double mCritHeShell = WD_BELCZYNSKI_MINIMUM_HE_CONSTANT - WD_BELCZYNSKI_MINIMUM_HE_LINEAR * Mdot;// Minimum shell mass of He for ignition. Eq 61, Belczynski+ 2008. This helium should not be burnt, but not implemented this yet. Ruiter+ 2014. if (utils::Compare(m_HeShell, mCritHeShell) >= 0) { m_ShouldRejuvenate = true; } diff --git a/src/HeWD.h b/src/HeWD.h index 8d990e413..9c73909d2 100755 --- a/src/HeWD.h +++ b/src/HeWD.h @@ -41,7 +41,7 @@ class HeWD: virtual public BaseStar, public WhiteDwarfs { return WhiteDwarfs::CalculateLuminosityOnPhase_Static(p_Mass, p_Time, p_Metallicity, WD_Baryon_Number.at(STELLAR_TYPE::HELIUM_WHITE_DWARF)); } - ACCRETION_REGIME DetermineAccretionRegime(const bool p_HeRich, const double p_DonorThermalMassLossRate); + ACCRETION_REGIME DetermineAccretionRegime(const double p_DonorThermalMassLossRate, const bool p_HeRich); protected: diff --git a/src/Log.cpp b/src/Log.cpp index b50e08b69..65f2ef4d2 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -79,7 +79,7 @@ bool Log::OpenHDF5RunDetailsFile(const string p_Filename) { // open the run details file inside the HDF5 container string h5GroupName = p_Filename; // HDF5 group name for run details file h5GroupName = utils::trim(h5GroupName); // remove leading and trailing blanks - hid_t h5GroupId = H5Gopen(m_Run_Details_H5_File.fileId, h5GroupName.c_str(), H5P_DEFAULT); // open the group + hid_t h5GroupId = H5Gopen(m_Run_Details_H5_File.fileId, h5GroupName.c_str(), H5P_DEFAULT); // open the group if (h5GroupId >= 0) { // group open (and therefore already exists)? Squawk("ERROR: HDF5 group with name " + h5GroupName + " already exists"); // that's not ok - announce error (void)H5Gclose(h5GroupId); // close the group @@ -146,9 +146,9 @@ bool Log::OpenHDF5RunDetailsFile(const string p_Filename) { } if (ok) { // have valid property - h5DatasetName = std::get<0>(runDetails); // dataset name + h5DatasetName = std::get<0>(runDetails); // dataset name TYPENAME compasType = std::get<1>(runDetails); // COMPAS data type - h5DataType = GetHDF5DataType(compasType, std::get<2>(runDetails)); // HDF5 data type + h5DataType = GetHDF5DataType(compasType, std::get<2>(runDetails)); // HDF5 data type h5Dset = CreateHDF5Dataset(m_HDF5ContainerName, h5GroupId, h5DatasetName, h5DataType, "-", chunkSize); // create dataset if (h5Dset < 0) { // dataset not created Squawk("ERROR: Error creating HDF5 dataset with name " + h5DatasetName); // announce error @@ -160,7 +160,7 @@ bool Log::OpenHDF5RunDetailsFile(const string p_Filename) { // derivation h5DatasetName += "-Derivation"; // derivation - h5Dset = CreateHDF5Dataset(m_HDF5ContainerName, h5GroupId, h5DatasetName, h5String13DataType, "-", chunkSize); // create dataset + h5Dset = CreateHDF5Dataset(m_HDF5ContainerName, h5GroupId, h5DatasetName, h5String13DataType, "-", chunkSize); // create dataset if (h5Dset < 0) { // dataset not created Squawk("ERROR: Error creating HDF5 dataset with name " + h5DatasetName); // announce error ok = false; // fail @@ -177,9 +177,9 @@ bool Log::OpenHDF5RunDetailsFile(const string p_Filename) { for (std::size_t idx = 0; idx < m_OptionDetails.size(); idx++) { // for each program option // option TYPENAME compasType = m_OptionDetails[idx].dataType; // COMPAS data type - h5DataType = GetHDF5DataType(compasType, (m_OptionDetails[idx].valueStr).length()); // HDF5 data type for COMPAS data type - h5DatasetName = m_OptionDetails[idx].optionStr; // dataset (option name) - h5Dset = CreateHDF5Dataset(m_HDF5ContainerName, h5GroupId, h5DatasetName, h5DataType, "-", chunkSize); // create dataset + h5DataType = GetHDF5DataType(compasType, (m_OptionDetails[idx].valueStr).length()); // HDF5 data type for COMPAS data type + h5DatasetName = m_OptionDetails[idx].optionStr; // dataset (option name) + h5Dset = CreateHDF5Dataset(m_HDF5ContainerName, h5GroupId, h5DatasetName, h5DataType, "-", chunkSize); // create dataset if (h5Dset < 0) { // dataset not created Squawk("ERROR: Error creating HDF5 dataset with name " + h5DatasetName); // announce error ok = false; // fail @@ -190,7 +190,7 @@ bool Log::OpenHDF5RunDetailsFile(const string p_Filename) { // derivation h5DatasetName += "-Derivation"; // derivation - h5Dset = CreateHDF5Dataset(m_HDF5ContainerName, h5GroupId, h5DatasetName, h5String13DataType, "-", chunkSize); // create dataset + h5Dset = CreateHDF5Dataset(m_HDF5ContainerName, h5GroupId, h5DatasetName, h5String13DataType, "-", chunkSize); // create dataset if (h5Dset < 0) { // dataset not created Squawk("ERROR: Error creating HDF5 dataset with name " + h5DatasetName); // announce error ok = false; // fail @@ -284,21 +284,23 @@ void Log::Start(const string p_LogBasePathString, // may be changed if a logfile definitions file is present and processed. // BSE - if (NotesPropertyPresent(m_BSE_CEE_Rec )) m_BSE_CEE_Notes = BOOL_VECTOR(m_BSE_CEE_Notes.size(), true); - if (NotesPropertyPresent(m_BSE_DCO_Rec )) m_BSE_DCO_Notes = BOOL_VECTOR(m_BSE_DCO_Notes.size(), true); - if (NotesPropertyPresent(m_BSE_Detailed_Rec )) m_BSE_Detailed_Notes = BOOL_VECTOR(m_BSE_Detailed_Notes.size(), true); - if (NotesPropertyPresent(m_BSE_Pulsars_Rec )) m_BSE_Pulsars_Notes = BOOL_VECTOR(m_BSE_Pulsars_Notes.size(), true); - if (NotesPropertyPresent(m_BSE_RLOF_Rec )) m_BSE_RLOF_Notes = BOOL_VECTOR(m_BSE_RLOF_Notes.size(), true); - if (NotesPropertyPresent(m_BSE_SNE_Rec )) m_BSE_SNE_Notes = BOOL_VECTOR(m_BSE_SNE_Notes.size(), true); - if (NotesPropertyPresent(m_BSE_Switch_Rec )) m_BSE_Switch_Notes = BOOL_VECTOR(m_BSE_Switch_Notes.size(), true); - if (NotesPropertyPresent(m_BSE_SysParms_Rec )) m_BSE_SysParms_Notes = BOOL_VECTOR(m_BSE_SysParms_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_CEE_Rec )) m_BSE_CEE_Notes = BOOL_VECTOR(m_BSE_CEE_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_DCO_Rec )) m_BSE_DCO_Notes = BOOL_VECTOR(m_BSE_DCO_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_Detailed_Rec )) m_BSE_Detailed_Notes = BOOL_VECTOR(m_BSE_Detailed_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_Pulsars_Rec )) m_BSE_Pulsars_Notes = BOOL_VECTOR(m_BSE_Pulsars_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_RLOF_Rec )) m_BSE_RLOF_Notes = BOOL_VECTOR(m_BSE_RLOF_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_SNE_Rec )) m_BSE_SNE_Notes = BOOL_VECTOR(m_BSE_SNE_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_Switch_Rec )) m_BSE_Switch_Notes = BOOL_VECTOR(m_BSE_Switch_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_SysParms_Rec )) m_BSE_SysParms_Notes = BOOL_VECTOR(m_BSE_SysParms_Notes.size(), true); + if (NotesPropertyPresent(m_BSE_Sys_Snapshot_Rec)) m_BSE_Sys_Snapshot_Notes = BOOL_VECTOR(m_BSE_Sys_Snapshot_Notes.size(), true); // SSE - if (NotesPropertyPresent(m_SSE_Detailed_Rec )) m_SSE_Detailed_Notes = BOOL_VECTOR(m_SSE_Detailed_Notes.size(), true); - if (NotesPropertyPresent(m_SSE_SNE_Rec )) m_SSE_SNE_Notes = BOOL_VECTOR(m_SSE_SNE_Notes.size(), true); - if (NotesPropertyPresent(m_SSE_Switch_Rec )) m_SSE_Switch_Notes = BOOL_VECTOR(m_SSE_Switch_Notes.size(), true); - if (NotesPropertyPresent(m_SSE_SysParms_Rec )) m_SSE_SysParms_Notes = BOOL_VECTOR(m_SSE_SysParms_Notes.size(), true); - if (NotesPropertyPresent(m_SSE_Pulsars_Rec )) m_SSE_Pulsars_Notes = BOOL_VECTOR(m_SSE_Pulsars_Notes.size(), true); + if (NotesPropertyPresent(m_SSE_Detailed_Rec )) m_SSE_Detailed_Notes = BOOL_VECTOR(m_SSE_Detailed_Notes.size(), true); + if (NotesPropertyPresent(m_SSE_Pulsars_Rec )) m_SSE_Pulsars_Notes = BOOL_VECTOR(m_SSE_Pulsars_Notes.size(), true); + if (NotesPropertyPresent(m_SSE_SNE_Rec )) m_SSE_SNE_Notes = BOOL_VECTOR(m_SSE_SNE_Notes.size(), true); + if (NotesPropertyPresent(m_SSE_Switch_Rec )) m_SSE_Switch_Notes = BOOL_VECTOR(m_SSE_Switch_Notes.size(), true); + if (NotesPropertyPresent(m_SSE_SysParms_Rec )) m_SSE_SysParms_Notes = BOOL_VECTOR(m_SSE_SysParms_Notes.size(), true); + if (NotesPropertyPresent(m_SSE_Sys_Snapshot_Rec)) m_SSE_Sys_Snapshot_Notes = BOOL_VECTOR(m_SSE_Sys_Snapshot_Notes.size(), true); // process the logfile definitions file if specified m_Enabled = UpdateAllLogfileRecordSpecs(); // update all logfile record specifications - disable logging upon failure @@ -1340,7 +1342,7 @@ bool Log::WriteHDF5_(h5AttrT& p_H5file, const string p_H5filename, const size_t case TYPENAME::STELLAR_TYPE : v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; case TYPENAME::MT_CASE : v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; case TYPENAME::MT_TRACKING : v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; - case TYPENAME::MASS_TRANSFER_TIMESCALE : v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; + case TYPENAME::MT_TIMESCALE : v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; case TYPENAME::SN_EVENT : v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; case TYPENAME::SN_STATE : v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; case TYPENAME::EVOLUTION_STATUS: v = static_cast(boost::get(p_H5file.dataSets[p_DataSetIdx].buf[i])); break; @@ -1440,7 +1442,7 @@ bool Log::WriteHDF5_(h5AttrT& p_H5file, const string p_H5filename, const size_t strcpy(cBuf[i], buf[i].c_str()); // copy chars + null terminator } - ok = H5Dwrite(dSet, dType, h5Dspace, h5FSpace, H5P_DEFAULT, (const void *)cBuf);; // write the data + ok = H5Dwrite(dSet, dType, h5Dspace, h5FSpace, H5P_DEFAULT, (const void *)cBuf); // write the data // release allocated memory for (size_t i = 0; i < bufSize; i++) { @@ -2099,6 +2101,11 @@ std::tuple Log::GetStandardLogFile annotations = m_BSE_Switch_Notes; // logfile annotations break; + case LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG: // BSE_SYSTEM_SNAPSHOT_LOG + recordProperties = m_BSE_Sys_Snapshot_Rec; // record properties + annotations = m_BSE_Sys_Snapshot_Notes; // logfile annotations + break; + case LOGFILE::BSE_SYSTEM_PARAMETERS: // BSE_SYSTEM_PARAMETERS recordProperties = m_BSE_SysParms_Rec; // record properties annotations = m_BSE_SysParms_Notes; // logfile annotations @@ -2120,8 +2127,8 @@ std::tuple Log::GetStandardLogFile break; case LOGFILE::SSE_DETAILED_OUTPUT: // SSE_DETAILED_OUTPUT - recordProperties = m_SSE_SNE_Rec; // record properties - annotations = m_SSE_SNE_Notes; // logfile annotations + recordProperties = m_SSE_Detailed_Rec; // record properties + annotations = m_SSE_Detailed_Notes; // logfile annotations break; case LOGFILE::SSE_SUPERNOVAE: // SSE_SUPERNOVAE @@ -2139,6 +2146,11 @@ std::tuple Log::GetStandardLogFile annotations = m_SSE_Pulsars_Notes; // logfile annotations break; + case LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG: // SSE_SYSTEM_SNAPSHOT_LOG + recordProperties = m_SSE_Sys_Snapshot_Rec; // record properties + annotations = m_SSE_Sys_Snapshot_Notes; // logfile annotations + break; + case LOGFILE::SSE_SYSTEM_PARAMETERS: // SSE_SYSTEM_PARAMETERS recordProperties = m_SSE_SysParms_Rec; // record properties annotations = m_SSE_SysParms_Notes; // logfile annotations @@ -2261,24 +2273,24 @@ hid_t Log::GetHDF5DataType(const TYPENAME p_COMPASdatatype, const int p_FieldWid hid_t h5DataType = -1; // HDF5 datatype - return value switch (p_COMPASdatatype) { // which COMPAS datatype? - case TYPENAME::SHORTINT : h5DataType = H5T_NATIVE_SHORT; break; - case TYPENAME::INT : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::LONGINT : h5DataType = H5T_NATIVE_LONG; break; - case TYPENAME::USHORTINT : h5DataType = H5T_NATIVE_USHORT; break; - case TYPENAME::UINT : h5DataType = H5T_NATIVE_UINT; break; - case TYPENAME::ULONGINT : h5DataType = H5T_NATIVE_ULONG; break; - case TYPENAME::FLOAT : h5DataType = H5T_NATIVE_FLOAT; break; - case TYPENAME::DOUBLE : h5DataType = H5T_NATIVE_DOUBLE; break; - case TYPENAME::LONGDOUBLE : h5DataType = H5T_NATIVE_LDOUBLE; break; - case TYPENAME::OBJECT_ID : h5DataType = H5T_NATIVE_ULONG; break; - case TYPENAME::ERROR : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::STELLAR_TYPE : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::MT_CASE : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::MT_TRACKING : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::MASS_TRANSFER_TIMESCALE : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::SN_EVENT : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::SN_STATE : h5DataType = H5T_NATIVE_INT; break; - case TYPENAME::EVOLUTION_STATUS: h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::SHORTINT : h5DataType = H5T_NATIVE_SHORT; break; + case TYPENAME::INT : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::LONGINT : h5DataType = H5T_NATIVE_LONG; break; + case TYPENAME::USHORTINT : h5DataType = H5T_NATIVE_USHORT; break; + case TYPENAME::UINT : h5DataType = H5T_NATIVE_UINT; break; + case TYPENAME::ULONGINT : h5DataType = H5T_NATIVE_ULONG; break; + case TYPENAME::FLOAT : h5DataType = H5T_NATIVE_FLOAT; break; + case TYPENAME::DOUBLE : h5DataType = H5T_NATIVE_DOUBLE; break; + case TYPENAME::LONGDOUBLE : h5DataType = H5T_NATIVE_LDOUBLE; break; + case TYPENAME::OBJECT_ID : h5DataType = H5T_NATIVE_ULONG; break; + case TYPENAME::ERROR : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::STELLAR_TYPE : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::MT_CASE : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::MT_TRACKING : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::MT_TIMESCALE : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::SN_EVENT : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::SN_STATE : h5DataType = H5T_NATIVE_INT; break; + case TYPENAME::EVOLUTION_STATUS : h5DataType = H5T_NATIVE_INT; break; case TYPENAME::STRING: { hid_t h5DType = H5Tcopy(H5T_C_S1); // HDF5 c-string datatype size_t size = p_StringQualifier == STRING_QUALIFIER::FIXED_LENGTH ? p_FieldWidth + 1 : H5T_VARIABLE; // size is dependent upon string type (fixed or variable length) @@ -2297,8 +2309,8 @@ hid_t Log::GetHDF5DataType(const TYPENAME p_COMPASdatatype, const int p_FieldWid h5DataType = H5T_NATIVE_UCHAR; } } break; - default: // unknown property type - Squawk(ERR_MSG(ERROR::UNKNOWN_DATA_TYPE)); // announce error + default: // unknown datatype + Squawk("Log::GetHDF5DataType(): " + ERR_MSG(ERROR::UNKNOWN_DATA_TYPE)); // announce error } return h5DataType; // HDF5 datatype @@ -2463,6 +2475,13 @@ LogfileDetailsT Log::StandardLogFileDetails(const LOGFILE p_Logfile, const strin fileDetails.annotations = m_BSE_Switch_Notes; break; + case LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG: // BSE_SYSTEM_SNAPSHOT_LOG + fileDetails.filename = OPTIONS->LogfileSystemSnapshotLog(); + fileDetails.recordTypes = OPTIONS->LogfileSystemSnapshotLogRecordTypes(); + fileDetails.recordProperties = m_BSE_Sys_Snapshot_Rec; + fileDetails.annotations = m_BSE_Sys_Snapshot_Notes; + break; + case LOGFILE::BSE_SYSTEM_PARAMETERS: // BSE_SYSTEM_PARAMETERS fileDetails.filename = OPTIONS->LogfileSystemParameters(); fileDetails.recordTypes = OPTIONS->LogfileSystemParametersRecordTypes(); @@ -2506,6 +2525,13 @@ LogfileDetailsT Log::StandardLogFileDetails(const LOGFILE p_Logfile, const strin fileDetails.annotations = m_SSE_Pulsars_Notes; break; + case LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG: // SSE_SYSTEM_SNAPSHOT_LOG + fileDetails.filename = OPTIONS->LogfileSystemSnapshotLog(); + fileDetails.recordTypes = OPTIONS->LogfileSystemSnapshotLogRecordTypes(); + fileDetails.recordProperties = m_SSE_Sys_Snapshot_Rec; + fileDetails.annotations = m_SSE_Sys_Snapshot_Notes; + break; + case LOGFILE::SSE_SYSTEM_PARAMETERS: // SSE_SYSTEM_PARAMETERS fileDetails.filename = OPTIONS->LogfileSystemParameters(); fileDetails.recordTypes = OPTIONS->LogfileSystemParametersRecordTypes(); @@ -3139,6 +3165,10 @@ void Log::UpdateLogfileRecordSpecs(const LOGFILE p_Logfile, if (p_UseDefaultProps) baseProps = m_BSE_Switch_Rec; baseNotes = m_BSE_Switch_Notes; break; + case LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG: + if (p_UseDefaultProps) baseProps = m_BSE_Sys_Snapshot_Rec; + baseNotes = m_BSE_Sys_Snapshot_Notes; + break; case LOGFILE::BSE_SYSTEM_PARAMETERS: if (p_UseDefaultProps) baseProps = m_BSE_SysParms_Rec; baseNotes = m_BSE_SysParms_Notes; @@ -3159,6 +3189,10 @@ void Log::UpdateLogfileRecordSpecs(const LOGFILE p_Logfile, if (p_UseDefaultProps) baseProps = m_SSE_Pulsars_Rec; baseNotes = m_SSE_Pulsars_Notes; break; + case LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG: + if (p_UseDefaultProps) baseProps = m_SSE_Sys_Snapshot_Rec; + baseNotes = m_SSE_Sys_Snapshot_Notes; + break; case LOGFILE::SSE_SYSTEM_PARAMETERS: if (p_UseDefaultProps) baseProps = m_SSE_SysParms_Rec; baseNotes = m_SSE_SysParms_Notes; @@ -3253,19 +3287,21 @@ void Log::UpdateLogfileRecordSpecs(const LOGFILE p_Logfile, // replace existing props and annotations vector for given logfile switch (p_Logfile) { - case LOGFILE::BSE_COMMON_ENVELOPES : m_BSE_CEE_Rec = newProps; m_BSE_CEE_Notes = newNotes; break; - case LOGFILE::BSE_DETAILED_OUTPUT : m_BSE_Detailed_Rec = newProps; m_BSE_Detailed_Notes = newNotes; break; - case LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS: m_BSE_DCO_Rec = newProps; m_BSE_DCO_Notes = newNotes; break; - case LOGFILE::BSE_PULSAR_EVOLUTION : m_BSE_Pulsars_Rec = newProps; m_BSE_Pulsars_Notes = newNotes; break; - case LOGFILE::BSE_RLOF_PARAMETERS : m_BSE_RLOF_Rec = newProps; m_BSE_RLOF_Notes = newNotes; break; - case LOGFILE::BSE_SUPERNOVAE : m_BSE_SNE_Rec = newProps; m_BSE_SNE_Notes = newNotes; break; - case LOGFILE::BSE_SWITCH_LOG : m_BSE_Switch_Rec = newProps; m_BSE_Switch_Notes = newNotes; break; - case LOGFILE::BSE_SYSTEM_PARAMETERS : m_BSE_SysParms_Rec = newProps; m_BSE_SysParms_Notes = newNotes; break; - case LOGFILE::SSE_DETAILED_OUTPUT : m_SSE_Detailed_Rec = newProps; m_SSE_Detailed_Notes = newNotes; break; - case LOGFILE::SSE_SUPERNOVAE : m_SSE_SNE_Rec = newProps; m_SSE_SNE_Notes = newNotes; break; - case LOGFILE::SSE_SWITCH_LOG : m_SSE_Switch_Rec = newProps; m_SSE_Switch_Notes = newNotes; break; - case LOGFILE::SSE_PULSAR_EVOLUTION : m_SSE_Pulsars_Rec = newProps; m_SSE_Pulsars_Notes = newNotes; break; - case LOGFILE::SSE_SYSTEM_PARAMETERS : m_SSE_SysParms_Rec = newProps; m_SSE_SysParms_Notes = newNotes; break; + case LOGFILE::BSE_COMMON_ENVELOPES : m_BSE_CEE_Rec = newProps; m_BSE_CEE_Notes = newNotes; break; + case LOGFILE::BSE_DETAILED_OUTPUT : m_BSE_Detailed_Rec = newProps; m_BSE_Detailed_Notes = newNotes; break; + case LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS: m_BSE_DCO_Rec = newProps; m_BSE_DCO_Notes = newNotes; break; + case LOGFILE::BSE_PULSAR_EVOLUTION : m_BSE_Pulsars_Rec = newProps; m_BSE_Pulsars_Notes = newNotes; break; + case LOGFILE::BSE_RLOF_PARAMETERS : m_BSE_RLOF_Rec = newProps; m_BSE_RLOF_Notes = newNotes; break; + case LOGFILE::BSE_SUPERNOVAE : m_BSE_SNE_Rec = newProps; m_BSE_SNE_Notes = newNotes; break; + case LOGFILE::BSE_SWITCH_LOG : m_BSE_Switch_Rec = newProps; m_BSE_Switch_Notes = newNotes; break; + case LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG : m_BSE_Sys_Snapshot_Rec = newProps; m_BSE_Sys_Snapshot_Notes = newNotes; break; + case LOGFILE::BSE_SYSTEM_PARAMETERS : m_BSE_SysParms_Rec = newProps; m_BSE_SysParms_Notes = newNotes; break; + case LOGFILE::SSE_DETAILED_OUTPUT : m_SSE_Detailed_Rec = newProps; m_SSE_Detailed_Notes = newNotes; break; + case LOGFILE::SSE_SUPERNOVAE : m_SSE_SNE_Rec = newProps; m_SSE_SNE_Notes = newNotes; break; + case LOGFILE::SSE_SWITCH_LOG : m_SSE_Switch_Rec = newProps; m_SSE_Switch_Notes = newNotes; break; + case LOGFILE::SSE_PULSAR_EVOLUTION : m_SSE_Pulsars_Rec = newProps; m_SSE_Pulsars_Notes = newNotes; break; + case LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG : m_SSE_Sys_Snapshot_Rec = newProps; m_SSE_Sys_Snapshot_Notes = newNotes; break; + case LOGFILE::SSE_SYSTEM_PARAMETERS : m_SSE_SysParms_Rec = newProps; m_SSE_SysParms_Notes = newNotes; break; default: break; // avoids compiler warning... } } diff --git a/src/Log.h b/src/Log.h index 440966165..3ca691105 100755 --- a/src/Log.h +++ b/src/Log.h @@ -396,12 +396,14 @@ class FormatVariantValue: public boost::static_visitor { string operator()(const STELLAR_TYPE v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const MT_CASE v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const MT_TRACKING v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } - string operator()(const MASS_TRANSFER_TIMESCALE v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } + string operator()(const MT_TIMESCALE v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const SN_EVENT v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const SN_STATE v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const EVOLUTION_STATUS v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const STR_VECTOR v, const string fmtStr) const { string fmt = fmtStr; fmt = "%-" + fmt + "s"; return utils::vFormat(fmt.c_str(), v[0].c_str()); } string operator()(const STR_VECTOR v, const string fmtStr, const size_t idx) const { string fmt = fmtStr; fmt = "%-" + fmt + "s"; return utils::vFormat(fmt.c_str(), v[idx].c_str()); } + string operator()(const DBL_VECTOR v, const string fmtStr) const { string fmt = fmtStr; fmt = "%" + fmt + "f"; return utils::vFormat(fmt.c_str(), v[0]); } + string operator()(const DBL_VECTOR v, const string fmtStr, const size_t idx) const { string fmt = fmtStr; fmt = "%" + fmt + "f"; return utils::vFormat(fmt.c_str(), v[idx]); } }; @@ -439,12 +441,14 @@ class FormatVariantValueDefault: public boost::static_visitor { string operator()(const STELLAR_TYPE v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const MT_CASE v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const MT_TRACKING v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } - string operator()(const MASS_TRANSFER_TIMESCALE v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } + string operator()(const MT_TIMESCALE v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const SN_EVENT v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const SN_STATE v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const EVOLUTION_STATUS v) const { string fmt = "%14.1d"; return utils::vFormat(fmt.c_str(), static_cast(v)); } string operator()(const STR_VECTOR v) const { string fmt = "%-30s"; return utils::vFormat(fmt.c_str(), v[0].c_str()); } string operator()(const STR_VECTOR v, const size_t idx) const { string fmt ="%-30s"; return utils::vFormat(fmt.c_str(), v[idx].c_str()); } + string operator()(const DBL_VECTOR v) const { string fmt = "%16.8e"; return utils::vFormat(fmt.c_str(), v[0]); } + string operator()(const DBL_VECTOR v, const size_t idx) const { string fmt = "%16.8e"; return utils::vFormat(fmt.c_str(), v[idx]); } }; @@ -480,12 +484,14 @@ class Log { m_PrimarySwitching = false; // Star switching is primary star of binary - default false m_SwitchIsMerger = false; // Switchlog record records a merger (rather than a simple switch) +/* m_SSESupernovae_DelayedWrite.logRecordType = 0; // delayed log record type for SSE_Supernovae file - initially 0 (set later) m_SSESupernovae_DelayedWrite.logRecordString = ""; // delayed log record (string) for SSE_Supernovae file - initially empty m_SSESupernovae_DelayedWrite.logRecordValues = {}; // delayed log record (property values) for SSE_Supernovae file - initially empty m_SSESupernovae_DelayedWrite.logRecordProperties = {}; // SSE Supernovae logfile record properties - initially empty m_SSESupernovae_DelayedWrite.logRecordFmtVector = {}; // SSE Supernovae logfile format vector - initially empty m_SSESupernovae_DelayedWrite.logFileAnnotations = {}; // SSE Supernovae annotations vector - initially empty +*/ m_OptionDetails = {}; // option details retrieved from commandline - initially empty }; @@ -563,21 +569,23 @@ class Log { // logfile record specifications // BSE - ANY_PROPERTY_VECTOR m_BSE_CEE_Rec = BSE_COMMON_ENVELOPES_REC; // default specification - ANY_PROPERTY_VECTOR m_BSE_DCO_Rec = BSE_DOUBLE_COMPACT_OBJECTS_REC; // default specification - ANY_PROPERTY_VECTOR m_BSE_Detailed_Rec = BSE_DETAILED_OUTPUT_REC; // default specification - ANY_PROPERTY_VECTOR m_BSE_Pulsars_Rec = BSE_PULSAR_EVOLUTION_REC; // default specification - ANY_PROPERTY_VECTOR m_BSE_RLOF_Rec = BSE_RLOF_PARAMETERS_REC; // default specification - ANY_PROPERTY_VECTOR m_BSE_SNE_Rec = BSE_SUPERNOVAE_REC; // default specification - ANY_PROPERTY_VECTOR m_BSE_Switch_Rec = BSE_SWITCH_LOG_REC; // default specification - ANY_PROPERTY_VECTOR m_BSE_SysParms_Rec = BSE_SYSTEM_PARAMETERS_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_CEE_Rec = BSE_COMMON_ENVELOPES_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_DCO_Rec = BSE_DOUBLE_COMPACT_OBJECTS_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_Detailed_Rec = BSE_DETAILED_OUTPUT_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_Pulsars_Rec = BSE_PULSAR_EVOLUTION_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_RLOF_Rec = BSE_RLOF_PARAMETERS_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_SNE_Rec = BSE_SUPERNOVAE_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_Switch_Rec = BSE_SWITCH_LOG_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_SysParms_Rec = BSE_SYSTEM_PARAMETERS_REC; // default specification + ANY_PROPERTY_VECTOR m_BSE_Sys_Snapshot_Rec = BSE_SYSTEM_SNAPSHOT_LOG_REC; // default specification // SSE - ANY_PROPERTY_VECTOR m_SSE_Detailed_Rec = SSE_DETAILED_OUTPUT_REC; // default specification - ANY_PROPERTY_VECTOR m_SSE_SNE_Rec = SSE_SUPERNOVAE_REC; // default specification - ANY_PROPERTY_VECTOR m_SSE_Switch_Rec = SSE_SWITCH_LOG_REC; // default specification - ANY_PROPERTY_VECTOR m_SSE_SysParms_Rec = SSE_SYSTEM_PARAMETERS_REC; // default specification - ANY_PROPERTY_VECTOR m_SSE_Pulsars_Rec = SSE_PULSAR_EVOLUTION_REC; // default specification + ANY_PROPERTY_VECTOR m_SSE_Detailed_Rec = SSE_DETAILED_OUTPUT_REC; // default specification + ANY_PROPERTY_VECTOR m_SSE_Pulsars_Rec = SSE_PULSAR_EVOLUTION_REC; // default specification + ANY_PROPERTY_VECTOR m_SSE_SNE_Rec = SSE_SUPERNOVAE_REC; // default specification + ANY_PROPERTY_VECTOR m_SSE_Switch_Rec = SSE_SWITCH_LOG_REC; // default specification + ANY_PROPERTY_VECTOR m_SSE_SysParms_Rec = SSE_SYSTEM_PARAMETERS_REC; // default specification + ANY_PROPERTY_VECTOR m_SSE_Sys_Snapshot_Rec = SSE_SYSTEM_SNAPSHOT_LOG_REC; // default specification // logfile annotation specifications // @@ -594,21 +602,23 @@ class Log { // has the right defaults when processing any log definitions file. // BSE - BOOL_VECTOR m_BSE_CEE_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_BSE_DCO_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_BSE_Detailed_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_BSE_Pulsars_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_BSE_RLOF_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_BSE_SNE_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_BSE_Switch_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_BSE_SysParms_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_CEE_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_DCO_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_Detailed_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_Pulsars_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_RLOF_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_SNE_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_Switch_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_SysParms_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_BSE_Sys_Snapshot_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); // SSE - BOOL_VECTOR m_SSE_Detailed_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_SSE_SNE_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_SSE_Switch_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_SSE_SysParms_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); - BOOL_VECTOR m_SSE_Pulsars_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_SSE_Detailed_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_SSE_Pulsars_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_SSE_SNE_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_SSE_Switch_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_SSE_SysParms_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); + BOOL_VECTOR m_SSE_Sys_Snapshot_Notes = BOOL_VECTOR(OPTIONS->NotesHdrs().size(), false); // the following block of variables support the BSE Switch Log file @@ -628,7 +638,7 @@ class Log { // the discussion in the description of Log::GetStandardLogFileRecordDetails() in Log.cpp. // This functionality probably shouldn't be extended to allow queueing/delaying multiple records for later writing // (I don't think we need it, it would probably soak up too much memory if over-used, and it might just cause confusion) - +/* struct delayedWriteDetailsT { // attributes of delayed writes LOGRECORDTYPE logRecordType; // log record type string logRecordString; // log record to be written to log file in delayed write @@ -639,7 +649,7 @@ class Log { }; delayedWriteDetailsT m_SSESupernovae_DelayedWrite; // SSE_Supernovae delayed write details - +*/ // the following block of variables support the run details file @@ -1209,11 +1219,11 @@ class Log { template bool LogBSEPulsarEvolutionParameters(const T* const p_Binary, - const BSE_PULSAR_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_PULSAR_EVOLUTION)), 0, LOGFILE::BSE_PULSAR_EVOLUTION, static_cast(p_RecordType), p_Binary); } + const BSE_PULSAR_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_PULSAR_EVOLUTION)), 0, LOGFILE::BSE_PULSAR_EVOLUTION, static_cast(p_RecordType), p_Binary); } template bool LogBSESupernovaDetails(const T* const p_Binary, - const BSE_SN_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SUPERNOVAE)), 0, LOGFILE::BSE_SUPERNOVAE, static_cast(p_RecordType), p_Binary); } + const BSE_SN_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SUPERNOVAE)), 0, LOGFILE::BSE_SUPERNOVAE, static_cast(p_RecordType), p_Binary); } template bool LogBSESwitchLog(const T* const p_Binary, const bool p_PrimarySwitching, const bool p_IsMerger) { @@ -1224,96 +1234,47 @@ class Log { template bool LogBSESystemParameters(const T* const p_Binary, - const BSE_SYSPARMS_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SYSTEM_PARAMETERS)), 0, LOGFILE::BSE_SYSTEM_PARAMETERS, static_cast(p_RecordType), p_Binary); } + const BSE_SYSPARMS_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SYSTEM_PARAMETERS)), 0, LOGFILE::BSE_SYSTEM_PARAMETERS, static_cast(p_RecordType), p_Binary); } + template + bool LogBSESystemSnapshotLog(const T* const p_Binary, + const BSE_SYSTEM_SNAPSHOT_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG)), 0, LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG, static_cast(p_RecordType), p_Binary); } + template bool LogCommonEnvelope(const T* const p_Binary, - const CE_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_COMMON_ENVELOPES)), 0, LOGFILE::BSE_COMMON_ENVELOPES, static_cast(p_RecordType), p_Binary); } + const CE_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_COMMON_ENVELOPES)), 0, LOGFILE::BSE_COMMON_ENVELOPES, static_cast(p_RecordType), p_Binary); } template bool LogDoubleCompactObject(const T* const p_Binary, - const DCO_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS)), 0, LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS, static_cast(p_RecordType), p_Binary); } + const DCO_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS)), 0, LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS, static_cast(p_RecordType), p_Binary); } template bool LogRLOFParameters(const T* const p_Binary, - const RLOF_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_RLOF_PARAMETERS)), 0, LOGFILE::BSE_RLOF_PARAMETERS, static_cast(p_RecordType), p_Binary); } + const RLOF_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_RLOF_PARAMETERS)), 0, LOGFILE::BSE_RLOF_PARAMETERS, static_cast(p_RecordType), p_Binary); } template bool LogSSEDetailedOutput(const T* const p_Star, const int p_Id, - const SSE_DETAILED_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_DETAILED_OUTPUT)), 0, LOGFILE::SSE_DETAILED_OUTPUT, static_cast(p_RecordType), p_Star, "_" + std::to_string(abs(p_Id))); } + const SSE_DETAILED_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_DETAILED_OUTPUT)), 0, LOGFILE::SSE_DETAILED_OUTPUT, static_cast(p_RecordType), p_Star, "_" + std::to_string(abs(p_Id))); } template bool LogSSESupernovaDetails(const T* const p_Star, - const SSE_SN_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SUPERNOVAE)), 0, LOGFILE::SSE_SUPERNOVAE, static_cast(p_RecordType), p_Star); } + const SSE_SN_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SUPERNOVAE)), 0, LOGFILE::SSE_SUPERNOVAE, static_cast(p_RecordType), p_Star); } template - bool LogSSESwitchLog(const T* const p_Star) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SWITCH_LOG)), 0, LOGFILE::SSE_SWITCH_LOG, 1U, p_Star); } + bool LogSSESwitchLog(const T* const p_Star) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SWITCH_LOG)), 0, LOGFILE::SSE_SWITCH_LOG, 1U, p_Star); } template bool LogSSESystemParameters(const T* const p_Star, - const SSE_SYSPARMS_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SYSTEM_PARAMETERS)), 0, LOGFILE::SSE_SYSTEM_PARAMETERS, static_cast(p_RecordType), p_Star); } - - template - bool LogSSEPulsarEvolutionParameters(const T* const p_Star, - const SSE_PULSAR_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_PULSAR_EVOLUTION)), 0, LOGFILE::SSE_PULSAR_EVOLUTION, static_cast(p_RecordType), p_Star); } - - void ClearSSESupernovaStash() { - m_SSESupernovae_DelayedWrite.logRecordType = 0; // delayed log record type for SSE_Supernovae file - initially 0 (set later) - m_SSESupernovae_DelayedWrite.logRecordString = ""; // delayed log record (string) for SSE_Supernovae file - initially empty - m_SSESupernovae_DelayedWrite.logRecordValues = {}; // delayed log record (property values) for SSE_Supernovae file - initially empty - } + const SSE_SYSPARMS_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SYSTEM_PARAMETERS)), 0, LOGFILE::SSE_SYSTEM_PARAMETERS, static_cast(p_RecordType), p_Star); } template - void StashSSESupernovaDetails(const T* const p_Star, const STELLAR_TYPE p_StellarType, const SSE_SN_RECORD_TYPE p_RecordType) { - - m_SSESupernovae_DelayedWrite.logRecordType = static_cast(p_RecordType); - - // if we don't already have the SSE Supernova log record properties that we need, get them - // this will only need to be done once per run, so not a big overhead - if (m_SSESupernovae_DelayedWrite.logRecordProperties.empty() || - m_SSESupernovae_DelayedWrite.logRecordFmtVector.empty() || - m_SSESupernovae_DelayedWrite.logFileAnnotations.empty()) { - - std::tie(m_SSESupernovae_DelayedWrite.logRecordProperties, - m_SSESupernovae_DelayedWrite.logRecordFmtVector, - m_SSESupernovae_DelayedWrite.logFileAnnotations) = LOGGING->GetStandardLogFileRecordDetails(LOGFILE::SSE_SUPERNOVAE); - } - - // get a formatted record with current data - // this will replace any existing stashed record - no queue here - std::tie(m_SSESupernovae_DelayedWrite.logRecordString, - m_SSESupernovae_DelayedWrite.logRecordValues) = GetLogStandardRecord(LOGFILE::SSE_SUPERNOVAE, - m_SSESupernovae_DelayedWrite.logRecordType, - p_Star, - m_SSESupernovae_DelayedWrite.logRecordProperties, - m_SSESupernovae_DelayedWrite.logRecordFmtVector, - m_SSESupernovae_DelayedWrite.logFileAnnotations, - true, - (ANY_STAR_PROPERTY)STAR_PROPERTY::STELLAR_TYPE, - p_StellarType); - } - + bool LogSSESystemSnapshotLog(const T* const p_Star, + const SSE_SYSTEM_SNAPSHOT_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG)), 0, LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG, static_cast(p_RecordType), p_Star); } + template - bool LogStashedSSESupernovaDetails(const T* const p_Star) { - bool result = true; - - // if the stashed SSE Supernova record is non-empty, print it, then clear it - otherwise do nothing - - if (OPTIONS->LogfileType() == LOGFILETYPE::HDF5) { // logging to HDF5 file? - if (!m_SSESupernovae_DelayedWrite.logRecordValues.empty()) { // yes - need to log? - result = LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SUPERNOVAE)), 0, LOGFILE::SSE_SUPERNOVAE, m_SSESupernovae_DelayedWrite.logRecordType, p_Star, "", m_SSESupernovae_DelayedWrite.logRecordValues); - m_SSESupernovae_DelayedWrite.logRecordValues = {}; // clear record - } - } - else { // no - not HDF5 - if (!m_SSESupernovae_DelayedWrite.logRecordString.empty()) { // need to log? - result = LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SUPERNOVAE)), 0, LOGFILE::SSE_SUPERNOVAE, m_SSESupernovae_DelayedWrite.logRecordType, p_Star, "", m_SSESupernovae_DelayedWrite.logRecordString); - m_SSESupernovae_DelayedWrite.logRecordString = ""; // clear record - } - } + bool LogSSEPulsarEvolutionParameters(const T* const p_Star, + const SSE_PULSAR_RECORD_TYPE p_RecordType) { return LogStandardRecord(std::get<2>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_PULSAR_EVOLUTION)), 0, LOGFILE::SSE_PULSAR_EVOLUTION, static_cast(p_RecordType), p_Star); } - return result; - } }; #endif // __Log_h__ diff --git a/src/LogTypedefs.h b/src/LogTypedefs.h index 8fcb32bf9..cb99c198f 100644 --- a/src/LogTypedefs.h +++ b/src/LogTypedefs.h @@ -41,6 +41,7 @@ enum class TYPENAME: int { ULONGLONGINT, FLOAT, DOUBLE, + DOUBLE_VECTOR, LONGDOUBLE, STRING, OBJECT_ID, @@ -48,7 +49,7 @@ enum class TYPENAME: int { STELLAR_TYPE, MT_CASE, MT_TRACKING, - MASS_TRANSFER_TIMESCALE, + MT_TIMESCALE, SN_EVENT, SN_STATE, STRING_VECTOR, @@ -76,7 +77,7 @@ const COMPASUnorderedMap TYPENAME_LABEL = { { TYPENAME::STELLAR_TYPE, { "STELLAR_TYPE", "INT" }}, { TYPENAME::MT_CASE, { "MT_CASE", "INT" }}, { TYPENAME::MT_TRACKING, { "MT_TRACKING", "INT" }}, - { TYPENAME::MASS_TRANSFER_TIMESCALE, { "MASS_TRANSFER_TIMESCALE", "INT" }}, + { TYPENAME::MT_TIMESCALE, { "MT_TIMESCALE", "INT" }}, { TYPENAME::SN_EVENT, { "SN_EVENT", "INT" }}, { TYPENAME::SN_STATE, { "SN_STATE", "INT" }}, { TYPENAME::STRING_VECTOR, { "STRING_VECTOR", "VECTOR" }}, @@ -127,7 +128,7 @@ enum class STRING_QUALIFIER: int { NONE, FIXED_LENGTH, VARIABLE_LENGTH }; // !!! --logfile-definitions option. !!! // !!! !!! // !!! *NOTE* !!! -// !!! The following enum classes anad maps are not where header strings should !!! +// !!! The following enum classes and maps are not where header strings should !!! // !!! be changed! These classes and maps are a lookup facility for the logfile !!! // !!! definitions file parser. !!! // !!! !!! @@ -146,6 +147,7 @@ enum class STRING_QUALIFIER: int { NONE, FIXED_LENGTH, VARIABLE_LENGTH }; AGE, \ ANGULAR_MOMENTUM, \ BINDING_ENERGY_AT_COMMON_ENVELOPE, \ + BINDING_ENERGY_CONVECTIVE_ENVELOPE, \ BINDING_ENERGY_FIXED, \ BINDING_ENERGY_NANJING, \ BINDING_ENERGY_PRE_COMMON_ENVELOPE, \ @@ -156,6 +158,7 @@ enum class STRING_QUALIFIER: int { NONE, FIXED_LENGTH, VARIABLE_LENGTH }; CO_CORE_MASS, \ CO_CORE_MASS_AT_COMMON_ENVELOPE, \ CO_CORE_MASS_AT_COMPACT_OBJECT_FORMATION, \ + CONVECTIVE_ENV_MASS, \ CORE_MASS, \ CORE_MASS_AT_COMMON_ENVELOPE, \ CORE_MASS_AT_COMPACT_OBJECT_FORMATION, \ @@ -205,6 +208,7 @@ enum class STRING_QUALIFIER: int { NONE, FIXED_LENGTH, VARIABLE_LENGTH }; IS_USSN, \ KICK_MAGNITUDE, \ LAMBDA_AT_COMMON_ENVELOPE, \ + LAMBDA_CONVECTIVE_ENVELOPE, \ LAMBDA_DEWI, \ LAMBDA_FIXED, \ LAMBDA_KRUCKOW, \ @@ -245,7 +249,6 @@ enum class STRING_QUALIFIER: int { NONE, FIXED_LENGTH, VARIABLE_LENGTH }; RADIUS, \ RANDOM_SEED, \ RECYCLED_NEUTRON_STAR, \ - RLOF_ONTO_NS, \ ROCKET_KICK_MAGNITUDE, \ ROCKET_KICK_PHI, \ ROCKET_KICK_THETA, \ @@ -271,6 +274,9 @@ enum class STRING_QUALIFIER: int { NONE, FIXED_LENGTH, VARIABLE_LENGTH }; TOTAL_RADIUS_AT_COMPACT_OBJECT_FORMATION, \ TRUE_ANOMALY, \ TZAMS, \ + VELOCITY_X, \ + VELOCITY_Y, \ + VELOCITY_Z, \ ZETA_HURLEY, \ ZETA_HURLEY_HE, \ ZETA_SOBERMAN, \ @@ -293,6 +299,7 @@ const COMPASUnorderedMap STAR_PROPERTY_LABEL = { { STAR_PROPERTY::AGE, "AGE" }, { STAR_PROPERTY::ANGULAR_MOMENTUM, "ANGULAR_MOMENTUM" }, { STAR_PROPERTY::BINDING_ENERGY_AT_COMMON_ENVELOPE, "BINDING_ENERGY_AT_COMMON_ENVELOPE" }, + { STAR_PROPERTY::BINDING_ENERGY_CONVECTIVE_ENVELOPE, "BINDING_ENERGY_CONVECTIVE_ENVELOPE" }, { STAR_PROPERTY::BINDING_ENERGY_FIXED, "BINDING_ENERGY_FIXED" }, { STAR_PROPERTY::BINDING_ENERGY_NANJING, "BINDING_ENERGY_NANJING" }, { STAR_PROPERTY::BINDING_ENERGY_PRE_COMMON_ENVELOPE, "BINDING_ENERGY_PRE_COMMON_ENVELOPE" }, @@ -303,6 +310,7 @@ const COMPASUnorderedMap STAR_PROPERTY_LABEL = { { STAR_PROPERTY::CO_CORE_MASS, "CO_CORE_MASS" }, { STAR_PROPERTY::CO_CORE_MASS_AT_COMMON_ENVELOPE, "CO_CORE_MASS_AT_COMMON_ENVELOPE" }, { STAR_PROPERTY::CO_CORE_MASS_AT_COMPACT_OBJECT_FORMATION, "CO_CORE_MASS_AT_COMPACT_OBJECT_FORMATION" }, + { STAR_PROPERTY::CONVECTIVE_ENV_MASS, "CONVECTIVE_ENV_MASS" }, { STAR_PROPERTY::CORE_MASS, "CORE_MASS" }, { STAR_PROPERTY::CORE_MASS_AT_COMMON_ENVELOPE, "CORE_MASS_AT_COMMON_ENVELOPE" }, { STAR_PROPERTY::CORE_MASS_AT_COMPACT_OBJECT_FORMATION, "CORE_MASS_AT_COMPACT_OBJECT_FORMATION" }, @@ -353,6 +361,7 @@ const COMPASUnorderedMap STAR_PROPERTY_LABEL = { { STAR_PROPERTY::IS_USSN, "IS_USSN" }, { STAR_PROPERTY::KICK_MAGNITUDE, "KICK_MAGNITUDE" }, { STAR_PROPERTY::LAMBDA_AT_COMMON_ENVELOPE, "LAMBDA_AT_COMMON_ENVELOPE" }, + { STAR_PROPERTY::LAMBDA_CONVECTIVE_ENVELOPE, "LAMBDA_CONVECTIVE_ENVELOPE" }, { STAR_PROPERTY::LAMBDA_DEWI, "LAMBDA_DEWI" }, { STAR_PROPERTY::LAMBDA_FIXED, "LAMBDA_FIXED" }, { STAR_PROPERTY::LAMBDA_KRUCKOW, "LAMBDA_KRUCKOW" }, @@ -393,7 +402,6 @@ const COMPASUnorderedMap STAR_PROPERTY_LABEL = { { STAR_PROPERTY::RADIUS, "RADIUS" }, { STAR_PROPERTY::RANDOM_SEED, "RANDOM_SEED" }, { STAR_PROPERTY::RECYCLED_NEUTRON_STAR, "RECYCLED_NEUTRON_STAR" }, - { STAR_PROPERTY::RLOF_ONTO_NS, "RLOF_ONTO_NS" }, { STAR_PROPERTY::ROCKET_KICK_MAGNITUDE, "ROCKET_KICK_MAGNITUDE" }, { STAR_PROPERTY::ROCKET_KICK_PHI, "ROCKET_KICK_PHI" }, { STAR_PROPERTY::ROCKET_KICK_THETA, "ROCKET_KICK_THETA" }, @@ -419,6 +427,9 @@ const COMPASUnorderedMap STAR_PROPERTY_LABEL = { { STAR_PROPERTY::TOTAL_RADIUS_AT_COMPACT_OBJECT_FORMATION, "TOTAL_RADIUS_AT_COMPACT_OBJECT_FORMATION" }, { STAR_PROPERTY::TRUE_ANOMALY, "TRUE_ANOMALY" }, { STAR_PROPERTY::TZAMS, "TZAMS" }, + { STAR_PROPERTY::VELOCITY_X, "VELOCITY_X" }, + { STAR_PROPERTY::VELOCITY_Y, "VELOCITY_Y" }, + { STAR_PROPERTY::VELOCITY_Z, "VELOCITY_Z" }, { STAR_PROPERTY::ZETA_HURLEY, "ZETA_HURLEY" }, { STAR_PROPERTY::ZETA_HURLEY_HE, "ZETA_HURLEY_HE" }, { STAR_PROPERTY::ZETA_SOBERMAN, "ZETA_SOBERMAN" }, @@ -514,6 +525,10 @@ enum class BINARY_PROPERTY: int { RLOF_POST_MT_STAR2_MASS, RLOF_POST_MT_STAR1_RADIUS, RLOF_POST_MT_STAR2_RADIUS, + RLOF_POST_MT_STAR1_TEFF, + RLOF_POST_MT_STAR2_TEFF, + RLOF_POST_MT_STAR1_LUM, + RLOF_POST_MT_STAR2_LUM, RLOF_POST_MT_STAR1_RLOF, RLOF_POST_MT_STAR2_RLOF, RLOF_POST_MT_STAR1_STELLAR_TYPE, @@ -528,6 +543,10 @@ enum class BINARY_PROPERTY: int { RLOF_PRE_MT_STAR2_MASS, RLOF_PRE_MT_STAR1_RADIUS, RLOF_PRE_MT_STAR2_RADIUS, + RLOF_PRE_MT_STAR1_TEFF, + RLOF_PRE_MT_STAR2_TEFF, + RLOF_PRE_MT_STAR1_LUM, + RLOF_PRE_MT_STAR2_LUM, RLOF_PRE_MT_STAR1_RLOF, RLOF_PRE_MT_STAR2_RLOF, RLOF_PRE_MT_STAR1_STELLAR_TYPE, @@ -550,6 +569,7 @@ enum class BINARY_PROPERTY: int { SEMI_MAJOR_AXIS_AT_DCO_FORMATION, SEMI_MAJOR_AXIS_INITIAL, SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE, + SEMI_MAJOR_AXIS_POST_STAGE_1_CE, SEMI_MAJOR_AXIS_PRE_SUPERNOVA, SEMI_MAJOR_AXIS_PRE_SUPERNOVA_RSOL, SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE, @@ -569,11 +589,36 @@ enum class BINARY_PROPERTY: int { STELLAR_TYPE_NAME_2_PRE_COMMON_ENVELOPE, SUPERNOVA_ORBIT_INCLINATION_ANGLE, SUPERNOVA_STATE, - SYNCHRONIZATION_TIMESCALE, + SYNCHRONIZATION_TIMESCALE_1, + SYNCHRONIZATION_TIMESCALE_2, SYSTEMIC_SPEED, SYSTEMIC_VELOCITY_X, SYSTEMIC_VELOCITY_Y, SYSTEMIC_VELOCITY_Z, + TIDAL_POTENTIAL_LOVE_NUMBER_10_1, + TIDAL_POTENTIAL_LOVE_NUMBER_12_1, + TIDAL_POTENTIAL_LOVE_NUMBER_22_1, + TIDAL_POTENTIAL_LOVE_NUMBER_32_1, + TIDAL_POTENTIAL_LOVE_NUMBER_10_2, + TIDAL_POTENTIAL_LOVE_NUMBER_12_2, + TIDAL_POTENTIAL_LOVE_NUMBER_22_2, + TIDAL_POTENTIAL_LOVE_NUMBER_32_2, + TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_1, + TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_1, + TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_1, + TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_1, + TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_2, + TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_2, + TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_2, + TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_2, + TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_1, + TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_1, + TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_1, + TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_1, + TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_2, + TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_2, + TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_2, + TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_2, TIME, TIME_TO_COALESCENCE, TOTAL_ANGULAR_MOMENTUM, @@ -628,7 +673,7 @@ const COMPASUnorderedMap BINARY_PROPERTY_LABEL = { { BINARY_PROPERTY::RANDOM_SEED, "RANDOM_SEED" }, { BINARY_PROPERTY::RLOF_ACCRETION_EFFICIENCY, "RLOF_ACCRETION_EFFICIENCY"}, { BINARY_PROPERTY::RLOF_MASS_LOSS_RATE, "RLOF_MASS_LOSS_RATE"}, - { BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, "RLOF_MASS_TRANSFER_TIMESCALE"}, + { BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, "RLOF_MASS_TRANSFER_TIMESCALE"}, { BINARY_PROPERTY::RLOF_POST_MT_COMMON_ENVELOPE, "RLOF_POST_MT_COMMON_ENVELOPE" }, { BINARY_PROPERTY::RLOF_POST_MT_ECCENTRICITY, "RLOF_POST_MT_ECCENTRICITY" }, { BINARY_PROPERTY::RLOF_POST_MT_EVENT_COUNTER, "RLOF_POST_MT_EVENT_COUNTER" }, @@ -638,6 +683,10 @@ const COMPASUnorderedMap BINARY_PROPERTY_LABEL = { { BINARY_PROPERTY::RLOF_POST_MT_STAR2_MASS, "RLOF_POST_MT_STAR2_MASS" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RADIUS, "RLOF_POST_MT_STAR1_RADIUS" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RADIUS, "RLOF_POST_MT_STAR2_RADIUS" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_TEFF, "RLOF_POST_MT_STAR1_TEFF" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_TEFF, "RLOF_POST_MT_STAR2_TEFF" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_LUM, "RLOF_POST_MT_STAR1_LUM" }, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_LUM, "RLOF_POST_MT_STAR2_LUM" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RLOF, "RLOF_POST_MT_STAR1_RLOF" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RLOF, "RLOF_POST_MT_STAR2_RLOF" }, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_STELLAR_TYPE, "RLOF_POST_MT_STAR1_STELLAR_TYPE" }, @@ -652,6 +701,10 @@ const COMPASUnorderedMap BINARY_PROPERTY_LABEL = { { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_MASS, "RLOF_PRE_MT_STAR2_MASS" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RADIUS, "RLOF_PRE_MT_STAR1_RADIUS" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RADIUS, "RLOF_PRE_MT_STAR2_RADIUS" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_TEFF, "RLOF_PRE_MT_STAR1_TEFF" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_TEFF, "RLOF_PRE_MT_STAR2_TEFF" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_LUM, "RLOF_PRE_MT_STAR1_LUM" }, + { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_LUM, "RLOF_PRE_MT_STAR2_LUM" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_RLOF, "RLOF_PRE_MT_STAR1_RLOF" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_RLOF, "RLOF_PRE_MT_STAR2_RLOF" }, { BINARY_PROPERTY::RLOF_PRE_MT_STAR1_STELLAR_TYPE, "RLOF_PRE_MT_STAR1_STELLAR_TYPE" }, @@ -674,6 +727,7 @@ const COMPASUnorderedMap BINARY_PROPERTY_LABEL = { { BINARY_PROPERTY::SEMI_MAJOR_AXIS_AT_DCO_FORMATION, "SEMI_MAJOR_AXIS_AT_DCO_FORMATION" }, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_INITIAL, "SEMI_MAJOR_AXIS_INITIAL" }, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE, "SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE" }, + { BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_STAGE_1_CE, "SEMI_MAJOR_AXIS_POST_STAGE_1_CE" }, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_SUPERNOVA, "SEMI_MAJOR_AXIS_PRE_SUPERNOVA" }, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_SUPERNOVA_RSOL, "SEMI_MAJOR_AXIS_PRE_SUPERNOVA_RSOL" }, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE, "SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE" }, @@ -693,11 +747,36 @@ const COMPASUnorderedMap BINARY_PROPERTY_LABEL = { { BINARY_PROPERTY::STELLAR_TYPE_NAME_2_PRE_COMMON_ENVELOPE, "STELLAR_TYPE_NAME_2_PRE_COMMON_ENVELOPE" }, { BINARY_PROPERTY::SUPERNOVA_ORBIT_INCLINATION_ANGLE, "SUPERNOVA_ORBIT_INCLINATION_ANGLE" }, { BINARY_PROPERTY::SUPERNOVA_STATE, "SUPERNOVA_STATE" }, - { BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE, "SYNCHRONIZATION_TIMESCALE" }, + { BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE_1, "SYNCHRONIZATION_TIMESCALE_1" }, + { BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE_2, "SYNCHRONIZATION_TIMESCALE_2" }, { BINARY_PROPERTY::SYSTEMIC_SPEED, "SYSTEMIC_SPEED" }, { BINARY_PROPERTY::SYSTEMIC_VELOCITY_X, "SYSTEMIC_VELOCITY_X" }, { BINARY_PROPERTY::SYSTEMIC_VELOCITY_Y, "SYSTEMIC_VELOCITY_Y" }, { BINARY_PROPERTY::SYSTEMIC_VELOCITY_Z, "SYSTEMIC_VELOCITY_Z" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_1, "TIDAL_POTENTIAL_LOVE_NUMBER_10_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_1, "TIDAL_POTENTIAL_LOVE_NUMBER_12_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_1, "TIDAL_POTENTIAL_LOVE_NUMBER_22_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_1, "TIDAL_POTENTIAL_LOVE_NUMBER_32_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_2, "TIDAL_POTENTIAL_LOVE_NUMBER_10_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_2, "TIDAL_POTENTIAL_LOVE_NUMBER_12_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_2, "TIDAL_POTENTIAL_LOVE_NUMBER_22_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_2, "TIDAL_POTENTIAL_LOVE_NUMBER_32_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_1, "TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_1, "TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_1, "TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_1, "TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_2, "TIDAL_POTENTIAL_LOVE_NUMBER_10_EQ_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_2, "TIDAL_POTENTIAL_LOVE_NUMBER_12_EQ_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_2, "TIDAL_POTENTIAL_LOVE_NUMBER_22_EQ_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_2, "TIDAL_POTENTIAL_LOVE_NUMBER_32_EQ_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_1, "TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_1, "TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_1, "TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_1, "TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_1" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_2, "TIDAL_POTENTIAL_LOVE_NUMBER_10_DYN_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_2, "TIDAL_POTENTIAL_LOVE_NUMBER_12_DYN_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_2, "TIDAL_POTENTIAL_LOVE_NUMBER_22_DYN_2" }, + { BINARY_PROPERTY::TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_2, "TIDAL_POTENTIAL_LOVE_NUMBER_32_DYN_2" }, { BINARY_PROPERTY::TIME, "TIME" }, { BINARY_PROPERTY::TIME_TO_COALESCENCE, "TIME_TO_COALESCENCE" }, { BINARY_PROPERTY::TOTAL_ANGULAR_MOMENTUM, "TOTAL_ANGULAR_MOMENTUM" }, @@ -746,6 +825,8 @@ enum class PROGRAM_OPTION: int { COMMON_ENVELOPE_MASS_ACCRETION_MIN, COMMON_ENVELOPE_MASS_ACCRETION_PRESCRIPTION, COMMON_ENVELOPE_RECOMBINATION_ENERGY_DENSITY, + COMMON_ENVELOPE_SECOND_STAGE_BETA, + COMMON_ENVELOPE_SECOND_STAGE_GAMMA_PRESCRIPTION, COMMON_ENVELOPE_SLOPE_KRUCKOW, CONVECTIVE_ENVELOPE_MASS_THRESHOLD, @@ -777,7 +858,7 @@ enum class PROGRAM_OPTION: int { INITIAL_MASS_FUNCTION, INITIAL_MASS_FUNCTION_MAX, INITIAL_MASS_FUNCTION_MIN, - INITIAL_MASS_FUNCTIONPOWER, + INITIAL_MASS_FUNCTION_POWER, KICK_DIRECTION_DISTRIBUTION, KICK_DIRECTION_POWER, @@ -807,6 +888,10 @@ enum class PROGRAM_OPTION: int { LBV_FACTOR, LBV_MASS_LOSS_PRESCRIPTION, + + MALTSEV_FALLBACK, + MALTSEV_MODE, + MASS_LOSS_PRESCRIPTION, MASS_RATIO, @@ -826,7 +911,7 @@ enum class PROGRAM_OPTION: int { METALLICITY_DISTRIBUTION_MAX, METALLICITY_DISTRIBUTION_MIN, - MINIMUM_MASS_SECONDARY, + MINIMUM_SAMPLED_SECONDARY_MASS, MT_ACCRETION_EFFICIENCY_PRESCRIPTION, MT_ANG_MOM_LOSS_PRESCRIPTION, @@ -851,14 +936,15 @@ enum class PROGRAM_OPTION: int { MT_FRACTION_ACCRETED, MT_JLOSS, - MT_JLOSS_MACLEOD_LINEAR_FRACTION_DEGEN, - MT_JLOSS_MACLEOD_LINEAR_FRACTION_NON_DEGEN, + MT_JLOSS_LINEAR_FRACTION_DEGEN, + MT_JLOSS_LINEAR_FRACTION_NON_DEGEN, MT_REJUVENATION_PRESCRIPTION, MT_THERMALLY_LIMITED_VARIATION, MULLER_MANDEL_KICK_MULTIPLIER_BH, MULLER_MANDEL_KICK_MULTIPLIER_NS, - MULLER_MANDEL_SIGMA_KICK, + MULLER_MANDEL_SIGMA_KICK_BH, + MULLER_MANDEL_SIGMA_KICK_NS, NEUTRINO_MASS_LOSS_ASSUMPTION_BH, NEUTRINO_MASS_LOSS_VALUE_BH, @@ -927,6 +1013,8 @@ enum class PROGRAM_OPTION: int { STELLAR_ZETA_PRESCRIPTION, TIDES_PRESCRIPTION, + + USSN_KICKS_OVERRIDE_MANDEL_MULLER, WR_FACTOR, @@ -974,6 +1062,8 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::COMMON_ENVELOPE_MASS_ACCRETION_MIN, "COMMON_ENVELOPE_MASS_ACCRETION_MIN" }, { PROGRAM_OPTION::COMMON_ENVELOPE_MASS_ACCRETION_PRESCRIPTION, "COMMON_ENVELOPE_MASS_ACCRETION_PRESCRIPTION" }, { PROGRAM_OPTION::COMMON_ENVELOPE_RECOMBINATION_ENERGY_DENSITY, "COMMON_ENVELOPE_RECOMBINATION_ENERGY_DENSITY" }, + { PROGRAM_OPTION::COMMON_ENVELOPE_SECOND_STAGE_BETA, "COMMON_ENVELOPE_SECOND_STAGE_BETA" }, + { PROGRAM_OPTION::COMMON_ENVELOPE_SECOND_STAGE_GAMMA_PRESCRIPTION, "COMMON_ENVELOPE_SECOND_STAGE_GAMMA_PRESCRIPTION" }, { PROGRAM_OPTION::COMMON_ENVELOPE_SLOPE_KRUCKOW, "COMMON_ENVELOPE_SLOPE_KRUCKOW" }, { PROGRAM_OPTION::COOL_WIND_MASS_LOSS_MULTIPLIER, "COOL_WIND_MASS_LOSS_MULTIPLIER" }, @@ -1002,7 +1092,7 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::INITIAL_MASS_FUNCTION, "INITIAL_MASS_FUNCTION" }, { PROGRAM_OPTION::INITIAL_MASS_FUNCTION_MAX, "INITIAL_MASS_FUNCTION_MAX" }, { PROGRAM_OPTION::INITIAL_MASS_FUNCTION_MIN, "INITIAL_MASS_FUNCTION_MIN" }, - { PROGRAM_OPTION::INITIAL_MASS_FUNCTIONPOWER, "INITIAL_MASS_FUNCTIONPOWER" }, + { PROGRAM_OPTION::INITIAL_MASS_FUNCTION_POWER, "INITIAL_MASS_FUNCTION_POWER" }, { PROGRAM_OPTION::KICK_DIRECTION_DISTRIBUTION, "KICK_DIRECTION_DISTRIBUTION" }, { PROGRAM_OPTION::KICK_DIRECTION_POWER, "KICK_DIRECTION_POWER" }, { PROGRAM_OPTION::KICK_SCALING_FACTOR, "KICK_SCALING_FACTOR" }, @@ -1031,6 +1121,9 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::LBV_FACTOR, "LBV_FACTOR" }, { PROGRAM_OPTION::LBV_MASS_LOSS_PRESCRIPTION, "LBV_MASS_LOSS_PRESCRIPTION" }, + + { PROGRAM_OPTION::MALTSEV_FALLBACK, "MALTSEV_FALLBACK" }, + { PROGRAM_OPTION::MALTSEV_MODE, "MALTSEV_MODE" }, { PROGRAM_OPTION::MASS_LOSS_PRESCRIPTION, "MASS_LOSS_PRESCRIPTION" }, { PROGRAM_OPTION::MASS_RATIO, "MASS_RATIO" }, @@ -1050,7 +1143,7 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::METALLICITY_DISTRIBUTION_MAX, "METALLICITY_DISTRIBUTION_MAX" }, { PROGRAM_OPTION::METALLICITY_DISTRIBUTION_MIN, "METALLICITY_DISTRIBUTION_MIN" }, - { PROGRAM_OPTION::MINIMUM_MASS_SECONDARY, "MINIMUM_MASS_SECONDARY" }, + { PROGRAM_OPTION::MINIMUM_SAMPLED_SECONDARY_MASS, "MINIMUM_SAMPLED_SECONDARY_MASS" }, { PROGRAM_OPTION::MT_ACCRETION_EFFICIENCY_PRESCRIPTION, "MT_ACCRETION_EFFICIENCY_PRESCRIPTION" }, { PROGRAM_OPTION::MT_ANG_MOM_LOSS_PRESCRIPTION, "MT_ANG_MOM_LOSS_PRESCRIPTION" }, @@ -1075,14 +1168,15 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::MT_FRACTION_ACCRETED, "MT_FRACTION_ACCRETED" }, { PROGRAM_OPTION::MT_JLOSS, "MT_JLOSS" }, - { PROGRAM_OPTION::MT_JLOSS_MACLEOD_LINEAR_FRACTION_DEGEN, "MT_JLOSS_MACLEOD_LINEAR_FRACTION_DEGEN" }, - { PROGRAM_OPTION::MT_JLOSS_MACLEOD_LINEAR_FRACTION_NON_DEGEN, "MT_JLOSS_MACLEOD_LINEAR_FRACTION_NON_DEGEN" }, + { PROGRAM_OPTION::MT_JLOSS_LINEAR_FRACTION_DEGEN, "MT_JLOSS_LINEAR_FRACTION_DEGEN" }, + { PROGRAM_OPTION::MT_JLOSS_LINEAR_FRACTION_NON_DEGEN, "MT_JLOSS_LINEAR_FRACTION_NON_DEGEN" }, { PROGRAM_OPTION::MT_REJUVENATION_PRESCRIPTION, "MT_REJUVENATION_PRESCRIPTION" }, { PROGRAM_OPTION::MT_THERMALLY_LIMITED_VARIATION, "MT_THERMALLY_LIMITED_VARIATION" }, { PROGRAM_OPTION::MULLER_MANDEL_KICK_MULTIPLIER_BH, "MULLER_MANDEL_KICK_MULTIPLIER_BH" }, { PROGRAM_OPTION::MULLER_MANDEL_KICK_MULTIPLIER_NS, "MULLER_MANDEL_KICK_MULTIPLIER_NS" }, - { PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK, "MULLER_MANDEL_SIGMA_KICK" }, + { PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK_BH, "MULLER_MANDEL_SIGMA_KICK_BH" }, + { PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK_NS, "MULLER_MANDEL_SIGMA_KICK_NS" }, { PROGRAM_OPTION::NEUTRINO_MASS_LOSS_ASSUMPTION_BH, "NEUTRINO_MASS_LOSS_ASSUMPTION_BH" }, { PROGRAM_OPTION::NEUTRINO_MASS_LOSS_VALUE_BH, "NEUTRINO_MASS_LOSS_VALUE_BH" }, @@ -1139,8 +1233,8 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::ROTATIONAL_FREQUENCY_1, "ROTATIONAL_FREQUENCY_1" }, { PROGRAM_OPTION::ROTATIONAL_FREQUENCY_2, "ROTATIONAL_FREQUENCY_2" }, - { PROGRAM_OPTION::SCALE_CHE_MASS_LOSS_SURF_HE_ABUNDANCE, "SCALE_CHE_MASS_LOSS_SURF_HE_ABUNDANCE" }, - { PROGRAM_OPTION::SCALE_TERMINAL_WIND_VEL_METALLICITY_POWER, "SCALE_TERMINAL_WIND_VEL_METALLICITY_POWER" }, + { PROGRAM_OPTION::SCALE_CHE_MASS_LOSS_SURF_HE_ABUNDANCE, "SCALE_CHE_MASS_LOSS_SURF_HE_ABUNDANCE" }, + { PROGRAM_OPTION::SCALE_TERMINAL_WIND_VEL_METALLICITY_POWER, "SCALE_TERMINAL_WIND_VEL_METALLICITY_POWER" }, { PROGRAM_OPTION::SEMI_MAJOR_AXIS, "SEMI_MAJOR_AXIS" }, { PROGRAM_OPTION::SEMI_MAJOR_AXIS_DISTRIBUTION, "SEMI_MAJOR_AXIS_DISTRIBUTION" }, { PROGRAM_OPTION::SEMI_MAJOR_AXIS_DISTRIBUTION_MAX, "SEMI_MAJOR_AXIS_DISTRIBUTION_MAX" }, @@ -1149,6 +1243,8 @@ const COMPASUnorderedMap PROGRAM_OPTION_LABEL = { { PROGRAM_OPTION::STELLAR_ZETA_PRESCRIPTION, "STELLAR_ZETA_PRESCRIPTION" }, { PROGRAM_OPTION::TIDES_PRESCRIPTION, "TIDES_PRESCRIPTION" }, + + { PROGRAM_OPTION::USSN_KICKS_OVERRIDE_MANDEL_MULLER, "USSN_KICKS_OVERRIDE_MANDEL_MULLER" }, { PROGRAM_OPTION::WR_FACTOR, "WR_FACTOR" }, @@ -1223,6 +1319,7 @@ const std::map ANY_STAR_PROPERTY_DETAIL = { { ANY_STAR_PROPERTY::AGE, { TYPENAME::DOUBLE, "Age", "Myr", 24, 15}}, { ANY_STAR_PROPERTY::ANGULAR_MOMENTUM, { TYPENAME::DOUBLE, "Ang_Momentum", "Msol AU^2 yr^-1", 24, 15}}, { ANY_STAR_PROPERTY::BINDING_ENERGY_AT_COMMON_ENVELOPE, { TYPENAME::DOUBLE, "Binding_Energy@CE", "erg", 24, 15}}, + { ANY_STAR_PROPERTY::BINDING_ENERGY_CONVECTIVE_ENVELOPE, { TYPENAME::DOUBLE, "BE_ConvectiveEnvelope", "erg", 24, 15}}, { ANY_STAR_PROPERTY::BINDING_ENERGY_FIXED, { TYPENAME::DOUBLE, "BE_Fixed", "erg", 24, 15}}, { ANY_STAR_PROPERTY::BINDING_ENERGY_NANJING, { TYPENAME::DOUBLE, "BE_Nanjing", "erg", 24, 15}}, { ANY_STAR_PROPERTY::BINDING_ENERGY_PRE_COMMON_ENVELOPE, { TYPENAME::DOUBLE, "Binding_Energy ANY_STAR_PROPERTY_DETAIL = { { ANY_STAR_PROPERTY::CO_CORE_MASS, { TYPENAME::DOUBLE, "Mass_CO_Core", "Msol", 24, 15}}, { ANY_STAR_PROPERTY::CO_CORE_MASS_AT_COMMON_ENVELOPE, { TYPENAME::DOUBLE, "Mass_CO_Core@CE", "Msol", 24, 15}}, { ANY_STAR_PROPERTY::CO_CORE_MASS_AT_COMPACT_OBJECT_FORMATION, { TYPENAME::DOUBLE, "Mass_CO_Core@CO", "Msol", 24, 15}}, + { ANY_STAR_PROPERTY::CONVECTIVE_ENV_MASS, { TYPENAME::DOUBLE, "Mass_Convective_Env", "Msol", 24, 15}}, { ANY_STAR_PROPERTY::CORE_MASS, { TYPENAME::DOUBLE, "Mass_Core", "Msol", 24, 15}}, { ANY_STAR_PROPERTY::CORE_MASS_AT_COMMON_ENVELOPE, { TYPENAME::DOUBLE, "Mass_Core@CE", "Msol", 24, 15}}, { ANY_STAR_PROPERTY::CORE_MASS_AT_COMPACT_OBJECT_FORMATION, { TYPENAME::DOUBLE, "Mass_Core@CO", "Msol", 24, 15}}, @@ -1282,6 +1380,7 @@ const std::map ANY_STAR_PROPERTY_DETAIL = { { ANY_STAR_PROPERTY::IS_USSN, { TYPENAME::BOOL, "USSN", "State", 0, 0 }}, { ANY_STAR_PROPERTY::KICK_MAGNITUDE, { TYPENAME::DOUBLE, "Applied_Kick_Magnitude", "kms^-1", 24, 15}}, { ANY_STAR_PROPERTY::LAMBDA_AT_COMMON_ENVELOPE, { TYPENAME::DOUBLE, "Lambda@CE", "-", 24, 15}}, + { ANY_STAR_PROPERTY::LAMBDA_CONVECTIVE_ENVELOPE, { TYPENAME::DOUBLE, "Lambda_Convective", "-", 24, 15}}, { ANY_STAR_PROPERTY::LAMBDA_DEWI, { TYPENAME::DOUBLE, "Lambda_Dewi", "-", 24, 15}}, { ANY_STAR_PROPERTY::LAMBDA_FIXED, { TYPENAME::DOUBLE, "Lambda_Fixed", "-", 24, 15}}, { ANY_STAR_PROPERTY::LAMBDA_KRUCKOW, { TYPENAME::DOUBLE, "Lambda_Kruckow", "-", 24, 15}}, @@ -1321,7 +1420,6 @@ const std::map ANY_STAR_PROPERTY_DETAIL = { { ANY_STAR_PROPERTY::RADIUS, { TYPENAME::DOUBLE, "Radius", "Rsol", 24, 15}}, { ANY_STAR_PROPERTY::RANDOM_SEED, { TYPENAME::ULONGINT, "SEED", "-", 12, 1 }}, { ANY_STAR_PROPERTY::RECYCLED_NEUTRON_STAR, { TYPENAME::BOOL, "Recycled_NS", "Event", 0, 0 }}, - { ANY_STAR_PROPERTY::RLOF_ONTO_NS, { TYPENAME::BOOL, "RLOF->NS", "Event", 0, 0 }}, { ANY_STAR_PROPERTY::ROCKET_KICK_MAGNITUDE, { TYPENAME::DOUBLE, "Rocket_Kick_Magnitude", "kms^-1", 24, 15}}, { ANY_STAR_PROPERTY::ROCKET_KICK_PHI, { TYPENAME::DOUBLE, "Rocket_Kick_Phi", "-", 24, 15}}, { ANY_STAR_PROPERTY::ROCKET_KICK_THETA, { TYPENAME::DOUBLE, "Rocket_Kick_Theta", "-", 24, 15}}, @@ -1348,6 +1446,9 @@ const std::map ANY_STAR_PROPERTY_DETAIL = { { ANY_STAR_PROPERTY::TOTAL_RADIUS_AT_COMPACT_OBJECT_FORMATION, { TYPENAME::DOUBLE, "Radius_Total@CO", "Rsol", 24, 15}}, { ANY_STAR_PROPERTY::TRUE_ANOMALY, { TYPENAME::DOUBLE, "True_Anomaly(psi)", "-", 24, 15}}, { ANY_STAR_PROPERTY::TZAMS, { TYPENAME::DOUBLE, "Teff@ZAMS", "K", 24, 15}}, + { ANY_STAR_PROPERTY::VELOCITY_X, { TYPENAME::DOUBLE, "VelocityX", "kms^-1", 24, 15}}, + { ANY_STAR_PROPERTY::VELOCITY_Y, { TYPENAME::DOUBLE, "VelocityY", "kms^-1", 24, 15}}, + { ANY_STAR_PROPERTY::VELOCITY_Z, { TYPENAME::DOUBLE, "VelocityZ", "kms^-1", 24, 15}}, { ANY_STAR_PROPERTY::ZETA_HURLEY, { TYPENAME::DOUBLE, "Zeta_Hurley", "-", 24, 15}}, { ANY_STAR_PROPERTY::ZETA_HURLEY_HE, { TYPENAME::DOUBLE, "Zeta_Hurley_He", "-", 24, 15}}, { ANY_STAR_PROPERTY::ZETA_SOBERMAN, { TYPENAME::DOUBLE, "Zeta_Soberman", "-", 24, 15}}, @@ -1387,9 +1488,9 @@ const std::map BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, { TYPENAME::MT_TRACKING, "MT_History", "-", 4, 1 }}, { BINARY_PROPERTY::MERGES_IN_HUBBLE_TIME, { TYPENAME::BOOL, "Merges_Hubble_Time", "State", 0, 0 }}, { BINARY_PROPERTY::OPTIMISTIC_COMMON_ENVELOPE, { TYPENAME::BOOL, "Optimistic_CE", "State", 0, 0 }}, - { BINARY_PROPERTY::ORBITAL_ANGULAR_MOMENTUM_VECTOR_X, { TYPENAME::DOUBLE, "Orbital_AM_Vector>SN_X", "-", 24, 15}}, - { BINARY_PROPERTY::ORBITAL_ANGULAR_MOMENTUM_VECTOR_Y, { TYPENAME::DOUBLE, "Orbital_AM_Vector>SN_Y", "-", 24, 15}}, - { BINARY_PROPERTY::ORBITAL_ANGULAR_MOMENTUM_VECTOR_Z, { TYPENAME::DOUBLE, "Orbital_AM_Vector>SN_Z", "-", 24, 15}}, + { BINARY_PROPERTY::ORBITAL_ANGULAR_MOMENTUM_VECTOR_X, { TYPENAME::DOUBLE, "Orbital_AM_Vector>SN_X", "-", 24, 15}}, + { BINARY_PROPERTY::ORBITAL_ANGULAR_MOMENTUM_VECTOR_Y, { TYPENAME::DOUBLE, "Orbital_AM_Vector>SN_Y", "-", 24, 15}}, + { BINARY_PROPERTY::ORBITAL_ANGULAR_MOMENTUM_VECTOR_Z, { TYPENAME::DOUBLE, "Orbital_AM_Vector>SN_Z", "-", 24, 15}}, { BINARY_PROPERTY::ORBITAL_ANGULAR_VELOCITY, { TYPENAME::DOUBLE, "Orbital_Angular_Velocity", "kms^-1", 24, 15}}, { BINARY_PROPERTY::ORBITAL_VELOCITY_PRE_SUPERNOVA, { TYPENAME::DOUBLE, "Orb_VelocityCE", "Rsol", 24, 15}}, @@ -1399,7 +1500,7 @@ const std::map BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::RANDOM_SEED, { TYPENAME::ULONGINT, "SEED", "-", 12, 1 }}, { BINARY_PROPERTY::RLOF_ACCRETION_EFFICIENCY, { TYPENAME::DOUBLE, "Beta", "-", 24, 15}}, { BINARY_PROPERTY::RLOF_MASS_LOSS_RATE, { TYPENAME::DOUBLE, "MassTransferRateDonor", "Msol/Myr", 24, 15}}, - { BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, { TYPENAME::MASS_TRANSFER_TIMESCALE, "MassTransferTimescale", "-", 4, 1}}, + { BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, { TYPENAME::MT_TIMESCALE, "MassTransferTimescale", "-", 4, 1 }}, { BINARY_PROPERTY::RLOF_POST_MT_COMMON_ENVELOPE, { TYPENAME::BOOL, "CEE>MT", "State", 0, 0 }}, { BINARY_PROPERTY::RLOF_POST_MT_ECCENTRICITY, { TYPENAME::DOUBLE, "Eccentricity>MT", "-", 24, 15}}, { BINARY_PROPERTY::RLOF_POST_MT_EVENT_COUNTER, { TYPENAME::UINT, "MT_Event_Counter", "Count", 6, 1 }}, @@ -1409,6 +1510,10 @@ const std::map BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::RLOF_POST_MT_STAR2_MASS, { TYPENAME::DOUBLE, "Mass(2)>MT", "Msol", 24, 15}}, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RADIUS, { TYPENAME::DOUBLE, "Radius(1)>MT", "Rsol", 24, 15}}, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RADIUS, { TYPENAME::DOUBLE, "Radius(2)>MT", "Rsol", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_TEFF, { TYPENAME::DOUBLE, "Teff(1)>MT", "K", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_TEFF, { TYPENAME::DOUBLE, "Teff(2)>MT", "K", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR1_LUM, { TYPENAME::DOUBLE, "Lum(1)>MT", "Lsol", 24, 15}}, + { BINARY_PROPERTY::RLOF_POST_MT_STAR2_LUM, { TYPENAME::DOUBLE, "Lum(2)>MT", "Lsol", 24, 15}}, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_RLOF, { TYPENAME::BOOL, "RLOF(1)>MT", "State", 0, 0 }}, { BINARY_PROPERTY::RLOF_POST_MT_STAR2_RLOF, { TYPENAME::BOOL, "RLOF(2)>MT", "State", 0, 0 }}, { BINARY_PROPERTY::RLOF_POST_MT_STAR1_STELLAR_TYPE, { TYPENAME::STELLAR_TYPE, "Stellar_Type(1)>MT", "-", 4, 1 }}, @@ -1423,6 +1528,10 @@ const std::map BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::RLOF_PRE_MT_STAR2_MASS, { TYPENAME::DOUBLE, "Mass(2) BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::SEMI_MAJOR_AXIS_AT_DCO_FORMATION, { TYPENAME::DOUBLE, "SemiMajorAxis@DCO", "AU", 24, 15}}, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_INITIAL, { TYPENAME::DOUBLE, "SemiMajorAxis@ZAMS", "AU", 24, 15}}, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE, { TYPENAME::DOUBLE, "SemiMajorAxis>CE", "Rsol", 24, 15}}, + { BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_STAGE_1_CE, { TYPENAME::DOUBLE, "SemiMajorAxisStage1>CE", "Rsol", 24, 15}}, { BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE, { TYPENAME::DOUBLE, "SemiMajorAxis BINARY_PROPERTY_DETAIL = { { BINARY_PROPERTY::STELLAR_TYPE_NAME_2_PRE_COMMON_ENVELOPE, { TYPENAME::STRING, "Stellar_Type(2) PROGRAM_OPTION_DETAIL = { { PROGRAM_OPTION::COMMON_ENVELOPE_MASS_ACCRETION_MIN, { TYPENAME::DOUBLE, "PO_CE_Mass_Accr_Min", "Msol", 24, 15}}, { PROGRAM_OPTION::COMMON_ENVELOPE_MASS_ACCRETION_PRESCRIPTION, { TYPENAME::INT, "PO_CE_Mass_Accr_Prscrptn", "-", 4, 1 }}, { PROGRAM_OPTION::COMMON_ENVELOPE_RECOMBINATION_ENERGY_DENSITY, { TYPENAME::DOUBLE, "PO_CE_Recomb_Enrgy_Dnsty", "erg g^-1", 24, 15}}, + { PROGRAM_OPTION::COMMON_ENVELOPE_SECOND_STAGE_BETA, { TYPENAME::DOUBLE, "PO_CE_Second_Stage_Beta", "-", 24, 15}}, + { PROGRAM_OPTION::COMMON_ENVELOPE_SECOND_STAGE_GAMMA_PRESCRIPTION, { TYPENAME::INT, "PO_CE_Second_Stage_Gamma_Prescription", "-", 24, 15}}, { PROGRAM_OPTION::COMMON_ENVELOPE_SLOPE_KRUCKOW, { TYPENAME::DOUBLE, "PO_CE_Slope_Kruckow", "-", 24, 15}}, { PROGRAM_OPTION::COOL_WIND_MASS_LOSS_MULTIPLIER, { TYPENAME::DOUBLE, "PO_Cool_WindMassLoss_Multipl", "-", 24, 15}}, @@ -1546,7 +1683,7 @@ const std::map PROGRAM_OPTION_DETAIL = { { PROGRAM_OPTION::INITIAL_MASS_FUNCTION, { TYPENAME::INT, "PO_Initial_Mass_Function", "-", 4, 1 }}, { PROGRAM_OPTION::INITIAL_MASS_FUNCTION_MAX, { TYPENAME::DOUBLE, "PO_Initial_Mass_Func_Max", "Msol", 24, 15}}, { PROGRAM_OPTION::INITIAL_MASS_FUNCTION_MIN, { TYPENAME::DOUBLE, "PO_Initial_Mass_Func_Min", "Msol", 24, 15}}, - { PROGRAM_OPTION::INITIAL_MASS_FUNCTIONPOWER, { TYPENAME::DOUBLE, "PO_Initial_Mass_Func_Power", "-", 24, 15}}, + { PROGRAM_OPTION::INITIAL_MASS_FUNCTION_POWER, { TYPENAME::DOUBLE, "PO_Initial_Mass_Func_Power", "-", 24, 15}}, { PROGRAM_OPTION::KICK_DIRECTION_DISTRIBUTION, { TYPENAME::INT, "PO_Kick_Direction_Dstrbtn", "-", 4, 1 }}, { PROGRAM_OPTION::KICK_DIRECTION_POWER, { TYPENAME::DOUBLE, "PO_Kick_Direction_Power", "-", 24, 15}}, @@ -1577,6 +1714,8 @@ const std::map PROGRAM_OPTION_DETAIL = { { PROGRAM_OPTION::LBV_FACTOR, { TYPENAME::DOUBLE, "PO_LBV_Factor", "-", 24, 15}}, { PROGRAM_OPTION::LBV_MASS_LOSS_PRESCRIPTION, { TYPENAME::INT, "PO_LBV_Mass_Loss_Prscrptn", "-", 4, 1 }}, + { PROGRAM_OPTION::MALTSEV_FALLBACK, { TYPENAME::DOUBLE, "PO_Maltsev_Fallback", "-", 24, 15}}, + { PROGRAM_OPTION::MALTSEV_MODE, { TYPENAME::INT, "PO_Maltsev_Mode", "-", 4, 1 }}, { PROGRAM_OPTION::MASS_LOSS_PRESCRIPTION, { TYPENAME::INT, "PO_Mass_Loss_Prscrptn", "-", 4, 1 }}, { PROGRAM_OPTION::MASS_RATIO, { TYPENAME::DOUBLE, "PO_Mass_Ratio", "-", 24, 15}}, @@ -1596,7 +1735,7 @@ const std::map PROGRAM_OPTION_DETAIL = { { PROGRAM_OPTION::METALLICITY_DISTRIBUTION_MAX, { TYPENAME::DOUBLE, "PO_Metallicity_Dstrbtn_Max", "-", 24, 15}}, { PROGRAM_OPTION::METALLICITY_DISTRIBUTION_MIN, { TYPENAME::DOUBLE, "PO_Metallicity_Dstrbtn_Min", "-", 24, 15}}, - { PROGRAM_OPTION::MINIMUM_MASS_SECONDARY, { TYPENAME::DOUBLE, "PO_Min_Secondary_Mass", "Msol", 24, 15}}, + { PROGRAM_OPTION::MINIMUM_SAMPLED_SECONDARY_MASS, { TYPENAME::DOUBLE, "PO_Min_Sampled_Secondary_Mass", "Msol", 24, 15}}, { PROGRAM_OPTION::MT_ACCRETION_EFFICIENCY_PRESCRIPTION, { TYPENAME::INT, "PO_MT_Acc_Efficiency_Prscrptn", "-", 4, 1 }}, { PROGRAM_OPTION::MT_ANG_MOM_LOSS_PRESCRIPTION, { TYPENAME::INT, "PO_MT_AngMom_Loss_Prscrptn", "-", 4, 1 }}, @@ -1622,14 +1761,15 @@ const std::map PROGRAM_OPTION_DETAIL = { { PROGRAM_OPTION::MT_FRACTION_ACCRETED, { TYPENAME::DOUBLE, "PO_MT_Fraction_Accreted", "-", 24, 15}}, { PROGRAM_OPTION::MT_JLOSS, { TYPENAME::DOUBLE, "PO_MT_JLoss", "-", 24, 15}}, - { PROGRAM_OPTION::MT_JLOSS_MACLEOD_LINEAR_FRACTION_DEGEN, { TYPENAME::DOUBLE, "PO_MT_JLoss_Macleod_Linear_Frac_Degen", "-", 24, 15}}, - { PROGRAM_OPTION::MT_JLOSS_MACLEOD_LINEAR_FRACTION_NON_DEGEN, { TYPENAME::DOUBLE, "PO_MT_JLoss_Macleod_Linear_Frac_Non_Degen", "-", 24, 15}}, + { PROGRAM_OPTION::MT_JLOSS_LINEAR_FRACTION_DEGEN, { TYPENAME::DOUBLE, "PO_MT_JLoss_Linear_Frac_Degen", "-", 24, 15}}, + { PROGRAM_OPTION::MT_JLOSS_LINEAR_FRACTION_NON_DEGEN, { TYPENAME::DOUBLE, "PO_MT_JLoss_Linear_Frac_Non_Degen", "-", 24, 15}}, { PROGRAM_OPTION::MT_REJUVENATION_PRESCRIPTION, { TYPENAME::INT, "PO_MT_Rejuvenation_Prscrptn", "-", 4, 1 }}, { PROGRAM_OPTION::MT_THERMALLY_LIMITED_VARIATION, { TYPENAME::INT, "PO_MT_Thermally_Lmtd_Variation", "-", 4, 1 }}, { PROGRAM_OPTION::MULLER_MANDEL_KICK_MULTIPLIER_BH, { TYPENAME::DOUBLE, "PO_MM_Kick_Multiplier_BH", "-", 24, 15}}, { PROGRAM_OPTION::MULLER_MANDEL_KICK_MULTIPLIER_NS, { TYPENAME::DOUBLE, "PO_MM_Kick_Multiplier_NS", "-", 24, 15}}, - { PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK, { TYPENAME::DOUBLE, "PO_MM_Sigma_Kick", "-", 24, 15}}, + { PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK_BH, { TYPENAME::DOUBLE, "PO_MM_Sigma_Kick_BH", "-", 24, 15}}, + { PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK_NS, { TYPENAME::DOUBLE, "PO_MM_Sigma_Kick_NS", "-", 24, 15}}, { PROGRAM_OPTION::NEUTRINO_MASS_LOSS_ASSUMPTION_BH, { TYPENAME::INT, "PO_Neutrino_Mass_Loss_Assmptn", "-", 4, 1 }}, { PROGRAM_OPTION::NEUTRINO_MASS_LOSS_VALUE_BH, { TYPENAME::DOUBLE, "PO_Neutrino_Mass_Loss_Value", "-", 24, 15}}, @@ -1696,6 +1836,8 @@ const std::map PROGRAM_OPTION_DETAIL = { { PROGRAM_OPTION::STELLAR_ZETA_PRESCRIPTION, { TYPENAME::INT, "PO_Stellar_Zeta_Prscrptn", "-", 4, 1 }}, { PROGRAM_OPTION::TIDES_PRESCRIPTION, { TYPENAME::INT, "PO_Tides_Prscrptn", "-", 4, 1 }}, + + { PROGRAM_OPTION::USSN_KICKS_OVERRIDE_MANDEL_MULLER, { TYPENAME::BOOL, "PO_USSN_Kicks_Override_Mandel_Muller", "flag", 0, 0}}, { PROGRAM_OPTION::WR_FACTOR, { TYPENAME::DOUBLE, "PO_WR_Factor", "-", 24, 15}}, @@ -1720,12 +1862,14 @@ enum class LOGFILE: int { BSE_RLOF_PARAMETERS, BSE_SUPERNOVAE, BSE_SWITCH_LOG, + BSE_SYSTEM_SNAPSHOT_LOG, BSE_SYSTEM_PARAMETERS, SSE_DETAILED_OUTPUT, SSE_PULSAR_EVOLUTION, SSE_SUPERNOVAE, SSE_SWITCH_LOG, + SSE_SYSTEM_SNAPSHOT_LOG, SSE_SYSTEM_PARAMETERS }; @@ -1743,7 +1887,7 @@ enum class DCO_RECORD_TYPE: unsigned int { }; enum class BSE_PULSAR_RECORD_TYPE: unsigned int { // BSE_PULSAR_EVOLUTION file record type - DEFAULT = 1, // 1 - default BSE_PULSAR_EVOLUTION file record type + PRE_SN = 1, // 1 - record was logged immediately prior to a supernova event POST_SN, // 2 - record was logged immediately following a supernova event POST_BINARY_TIMESTEP // 3 - record was logged immediately following binary timestep (i.e. the evolution of the binary system for a single timestep) }; @@ -1780,7 +1924,7 @@ enum class SSE_DETAILED_RECORD_TYPE: unsigned int { }; enum class SSE_PULSAR_RECORD_TYPE: unsigned int { // SSE_PULSAR_EVOLUTION file record type - DEFAULT = 1, // 1 - default SSE_PULSAR_EVOLUTION file record type + PRE_SN = 1, // 1 - record was logged immediately prior to a supernova event POST_SN, // 2 - record was logged immediately following a supernova event TIMESTEP_COMPLETED // 3 - record was logged immediately following the completion of the timestep (after all changes to the star) }; @@ -1801,6 +1945,14 @@ enum class SSE_SYSPARMS_RECORD_TYPE: unsigned int { DEFAULT = 1 // 1 - default SSE_SYSTEM_PARAMETERS file record type }; +enum class BSE_SYSTEM_SNAPSHOT_RECORD_TYPE: unsigned int { // BSE_SYSTEM_SNAPSHOT_LOG file record type + DEFAULT = 1 // 1 - default BSE_SYSTEM_SNAPSHOT_LOG file record type +}; + +enum class SSE_SYSTEM_SNAPSHOT_RECORD_TYPE: unsigned int { // SSE_SYSTEM_SNAPSHOT_LOG file record type + DEFAULT = 1 // 1 - default SSE_SYSTEM_SNAPSHOT_LOG file record type +}; + // enum class RUN_DETAILS_REC // symbolic names for RUN DETAILS record definitions @@ -1847,6 +1999,7 @@ const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { BINARY_PROPERTY::ECCENTRICITY_POST_COMMON_ENVELOPE, BINARY_PROPERTY::SEMI_MAJOR_AXIS_PRE_COMMON_ENVELOPE, BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_COMMON_ENVELOPE, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_POST_STAGE_1_CE, BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1_PRE_COMMON_ENVELOPE, BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1_POST_COMMON_ENVELOPE, BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2_PRE_COMMON_ENVELOPE, @@ -1858,6 +2011,7 @@ const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { BINARY_PROPERTY::RADIUS_1_POST_COMMON_ENVELOPE, BINARY_PROPERTY::STELLAR_TYPE_1_PRE_COMMON_ENVELOPE, STAR_1_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::LAMBDA_CONVECTIVE_ENVELOPE, STAR_1_PROPERTY::LAMBDA_FIXED, STAR_1_PROPERTY::LAMBDA_NANJING, STAR_1_PROPERTY::LAMBDA_LOVERIDGE, @@ -1868,6 +2022,7 @@ const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { STAR_1_PROPERTY::BINDING_ENERGY_LOVERIDGE, STAR_1_PROPERTY::BINDING_ENERGY_LOVERIDGE_WINDS, STAR_1_PROPERTY::BINDING_ENERGY_KRUCKOW, + STAR_1_PROPERTY::CONVECTIVE_ENV_MASS, BINARY_PROPERTY::MASS_2_PRE_COMMON_ENVELOPE, BINARY_PROPERTY::MASS_2_POST_COMMON_ENVELOPE, BINARY_PROPERTY::MASS_ENV_2, @@ -1875,6 +2030,7 @@ const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { BINARY_PROPERTY::RADIUS_2_POST_COMMON_ENVELOPE, BINARY_PROPERTY::STELLAR_TYPE_2_PRE_COMMON_ENVELOPE, STAR_2_PROPERTY::STELLAR_TYPE, + STAR_2_PROPERTY::LAMBDA_CONVECTIVE_ENVELOPE, STAR_2_PROPERTY::LAMBDA_FIXED, STAR_2_PROPERTY::LAMBDA_NANJING, STAR_2_PROPERTY::LAMBDA_LOVERIDGE, @@ -1885,6 +2041,7 @@ const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { STAR_2_PROPERTY::BINDING_ENERGY_LOVERIDGE, STAR_2_PROPERTY::BINDING_ENERGY_LOVERIDGE_WINDS, STAR_2_PROPERTY::BINDING_ENERGY_KRUCKOW, + STAR_2_PROPERTY::CONVECTIVE_ENV_MASS, BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, BINARY_PROPERTY::STELLAR_MERGER, BINARY_PROPERTY::OPTIMISTIC_COMMON_ENVELOPE, @@ -1902,7 +2059,8 @@ const ANY_PROPERTY_VECTOR BSE_COMMON_ENVELOPES_REC = { STAR_2_PROPERTY::THERMAL_TIMESCALE_PRE_COMMON_ENVELOPE, BINARY_PROPERTY::ZETA_STAR, BINARY_PROPERTY::ZETA_LOBE, - BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE, + BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE_1, + BINARY_PROPERTY::SYNCHRONIZATION_TIMESCALE_2, BINARY_PROPERTY::CIRCULARIZATION_TIMESCALE, STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE_PRE_COMMON_ENVELOPE, STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE_PRE_COMMON_ENVELOPE, @@ -1994,6 +2152,7 @@ const ANY_PROPERTY_VECTOR BSE_DETAILED_OUTPUT_REC = { STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE, STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE, BINARY_PROPERTY::RLOF_MASS_LOSS_RATE, + BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, BINARY_PROPERTY::RLOF_ACCRETION_EFFICIENCY }; @@ -2148,7 +2307,16 @@ const ANY_PROPERTY_VECTOR BSE_SUPERNOVAE_REC = { // const ANY_PROPERTY_VECTOR BSE_SWITCH_LOG_REC = { BINARY_PROPERTY::RANDOM_SEED, - BINARY_PROPERTY::TIME + BINARY_PROPERTY::TIME, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_RSOL, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2, + STAR_1_PROPERTY::MASS, + STAR_2_PROPERTY::MASS, + STAR_1_PROPERTY::STELLAR_TYPE, + STAR_2_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::RADIUS, + STAR_2_PROPERTY::RADIUS }; @@ -2173,6 +2341,7 @@ const ANY_PROPERTY_VECTOR BSE_SYSTEM_PARAMETERS_REC = { PROGRAM_OPTION::LBV_FACTOR, PROGRAM_OPTION::WR_FACTOR, PROGRAM_OPTION::COMMON_ENVELOPE_ALPHA, + PROGRAM_OPTION::COMMON_ENVELOPE_FORMALISM, STAR_1_PROPERTY::METALLICITY, STAR_2_PROPERTY::METALLICITY, BINARY_PROPERTY::UNBOUND, @@ -2191,6 +2360,94 @@ const ANY_PROPERTY_VECTOR BSE_SYSTEM_PARAMETERS_REC = { }; +// BSE_SYSTEM_SNAPSHOT_LOG_REC +// +// Default record definition for the BSE System Snapshot logfile +// +const ANY_PROPERTY_VECTOR BSE_SYSTEM_SNAPSHOT_LOG_REC = { + BINARY_PROPERTY::RANDOM_SEED, + BINARY_PROPERTY::DT, + BINARY_PROPERTY::TIME, + BINARY_PROPERTY::UNBOUND, + BINARY_PROPERTY::SEMI_MAJOR_AXIS_RSOL, + BINARY_PROPERTY::ECCENTRICITY, + STAR_1_PROPERTY::MZAMS, + STAR_2_PROPERTY::MZAMS, + STAR_1_PROPERTY::MASS_0, + STAR_2_PROPERTY::MASS_0, + STAR_1_PROPERTY::MASS, + STAR_2_PROPERTY::MASS, + STAR_1_PROPERTY::ENV_MASS, + STAR_2_PROPERTY::ENV_MASS, + STAR_1_PROPERTY::CORE_MASS, + STAR_2_PROPERTY::CORE_MASS, + STAR_1_PROPERTY::HE_CORE_MASS, + STAR_2_PROPERTY::HE_CORE_MASS, + STAR_1_PROPERTY::CO_CORE_MASS, + STAR_2_PROPERTY::CO_CORE_MASS, + STAR_1_PROPERTY::RADIUS, + STAR_2_PROPERTY::RADIUS, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_1, + BINARY_PROPERTY::ROCHE_LOBE_RADIUS_2, + STAR_1_PROPERTY::OMEGA, + STAR_2_PROPERTY::OMEGA, + STAR_1_PROPERTY::OMEGA_BREAK, + STAR_2_PROPERTY::OMEGA_BREAK, + STAR_1_PROPERTY::INITIAL_STELLAR_TYPE, + STAR_2_PROPERTY::INITIAL_STELLAR_TYPE, + STAR_1_PROPERTY::STELLAR_TYPE, + STAR_2_PROPERTY::STELLAR_TYPE, + STAR_1_PROPERTY::AGE, + STAR_2_PROPERTY::AGE, + STAR_1_PROPERTY::LUMINOSITY, + STAR_2_PROPERTY::LUMINOSITY, + STAR_1_PROPERTY::TEMPERATURE, + STAR_2_PROPERTY::TEMPERATURE, + STAR_1_PROPERTY::ANGULAR_MOMENTUM, + STAR_2_PROPERTY::ANGULAR_MOMENTUM, + STAR_1_PROPERTY::DYNAMICAL_TIMESCALE, + STAR_2_PROPERTY::DYNAMICAL_TIMESCALE, + STAR_1_PROPERTY::THERMAL_TIMESCALE, + STAR_2_PROPERTY::THERMAL_TIMESCALE, + STAR_1_PROPERTY::ZETA_SOBERMAN, + STAR_2_PROPERTY::ZETA_SOBERMAN, + STAR_1_PROPERTY::ZETA_SOBERMAN_HE, + STAR_2_PROPERTY::ZETA_SOBERMAN_HE, + STAR_1_PROPERTY::ZETA_HURLEY, + STAR_2_PROPERTY::ZETA_HURLEY, + STAR_1_PROPERTY::ZETA_HURLEY_HE, + STAR_2_PROPERTY::ZETA_HURLEY_HE, + STAR_1_PROPERTY::MASS_LOSS_DIFF, + STAR_2_PROPERTY::MASS_LOSS_DIFF, + STAR_1_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_2_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_1_PROPERTY::MASS_TRANSFER_DIFF, + STAR_2_PROPERTY::MASS_TRANSFER_DIFF, + STAR_1_PROPERTY::MDOT, + STAR_2_PROPERTY::MDOT, + BINARY_PROPERTY::TOTAL_ANGULAR_MOMENTUM, + BINARY_PROPERTY::TOTAL_ENERGY, + STAR_1_PROPERTY::METALLICITY, + STAR_2_PROPERTY::METALLICITY, + BINARY_PROPERTY::MASS_TRANSFER_TRACKER_HISTORY, + STAR_1_PROPERTY::PULSAR_MAGNETIC_FIELD, + STAR_2_PROPERTY::PULSAR_MAGNETIC_FIELD, + STAR_1_PROPERTY::PULSAR_SPIN_PERIOD, + STAR_2_PROPERTY::PULSAR_SPIN_PERIOD, + STAR_1_PROPERTY::PULSAR_SPIN_DOWN_RATE, + STAR_2_PROPERTY::PULSAR_SPIN_DOWN_RATE, + STAR_1_PROPERTY::PULSAR_BIRTH_PERIOD, + STAR_2_PROPERTY::PULSAR_BIRTH_PERIOD, + STAR_1_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, + STAR_2_PROPERTY::PULSAR_BIRTH_SPIN_DOWN_RATE, + STAR_1_PROPERTY::RADIAL_EXPANSION_TIMESCALE, + STAR_2_PROPERTY::RADIAL_EXPANSION_TIMESCALE, + BINARY_PROPERTY::RLOF_MASS_LOSS_RATE, + BINARY_PROPERTY::RLOF_MASS_TRANSFER_TIMESCALE, + BINARY_PROPERTY::RLOF_ACCRETION_EFFICIENCY +}; + + // SSE output record definitions // SSE_DETAILED_OUTPUT_REC @@ -2267,7 +2524,10 @@ const ANY_PROPERTY_VECTOR SSE_SUPERNOVAE_REC = { // const ANY_PROPERTY_VECTOR SSE_SWITCH_LOG_REC = { STAR_PROPERTY::RANDOM_SEED, - STAR_PROPERTY::TIME + STAR_PROPERTY::TIME, + STAR_PROPERTY::MASS, + STAR_PROPERTY::STELLAR_TYPE, + STAR_PROPERTY::RADIUS }; @@ -2297,6 +2557,34 @@ const ANY_PROPERTY_VECTOR SSE_SYSTEM_PARAMETERS_REC = { PROGRAM_OPTION::NOTES }; + +// SSE_SYSTEM_SNAPSHOT_LOG_REC +// +// Default record definition for the SSE System Snapshot logfile +// +const ANY_PROPERTY_VECTOR SSE_SYSTEM_SNAPSHOT_LOG_REC = { + STAR_PROPERTY::AGE, + STAR_PROPERTY::DT, + STAR_PROPERTY::TIME, + STAR_PROPERTY::STELLAR_TYPE, + STAR_PROPERTY::METALLICITY, + STAR_PROPERTY::MASS_0, + STAR_PROPERTY::MASS, + STAR_PROPERTY::RADIUS, + STAR_PROPERTY::RZAMS, + STAR_PROPERTY::LUMINOSITY, + STAR_PROPERTY::TEMPERATURE, + STAR_PROPERTY::CORE_MASS, + STAR_PROPERTY::CO_CORE_MASS, + STAR_PROPERTY::HE_CORE_MASS, + STAR_PROPERTY::MDOT, + STAR_PROPERTY::DOMINANT_MASS_LOSS_RATE, + STAR_PROPERTY::TIMESCALE_MS, + STAR_PROPERTY::OMEGA, + STAR_PROPERTY::OMEGA_BREAK +}; + + // enum class LOGFILE_CLASS // Symbolic names for logfile types enum class LOGFILE_CLASS: int { NONE, STELLAR, BINARY }; @@ -2307,25 +2595,27 @@ enum class LOGFILE_CLASS: int { NONE, STELLAR, BINARY }; // (the short names are for logfile definitions file parsing) typedef std::tuple LOGFILE_DESCRIPTOR_T; const std::map LOGFILE_DESCRIPTOR = { - { LOGFILE::NONE, { "" , {}, "", "", LOGFILE_CLASS::NONE}}, - - { LOGFILE::DEBUG_LOG, { "Debug_Log", {}, "", "", LOGFILE_CLASS::NONE }}, - { LOGFILE::ERROR_LOG, { "Error_Log", {}, "", "", LOGFILE_CLASS::NONE }}, - - { LOGFILE::BSE_COMMON_ENVELOPES, { "BSE_Common_Envelopes", BSE_COMMON_ENVELOPES_REC, "BSE_CEE", "BSE_CEE_REC", LOGFILE_CLASS::BINARY }}, - { LOGFILE::BSE_DETAILED_OUTPUT, { "BSE_Detailed_Output", BSE_DETAILED_OUTPUT_REC, "BSE_DETAILED", "BSE_DETAILED_REC", LOGFILE_CLASS::BINARY }}, - { LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS, { "BSE_Double_Compact_Objects", BSE_DOUBLE_COMPACT_OBJECTS_REC, "BSE_DCO", "BSE_DCO_REC", LOGFILE_CLASS::BINARY }}, - { LOGFILE::BSE_PULSAR_EVOLUTION, { "BSE_Pulsar_Evolution", BSE_PULSAR_EVOLUTION_REC, "BSE_PULSARS", "BSE_PULSARS_REC", LOGFILE_CLASS::BINARY }}, - { LOGFILE::BSE_RLOF_PARAMETERS, { "BSE_RLOF", BSE_RLOF_PARAMETERS_REC, "BSE_RLOF", "BSE_RLOF_REC", LOGFILE_CLASS::BINARY }}, - { LOGFILE::BSE_SUPERNOVAE, { "BSE_Supernovae", BSE_SUPERNOVAE_REC, "BSE_SNE", "BSE_SNE_REC", LOGFILE_CLASS::BINARY }}, - { LOGFILE::BSE_SWITCH_LOG, { "BSE_Switch_Log", BSE_SWITCH_LOG_REC, "BSE_SWITCH_LOG", "BSE_SWITCH_REC", LOGFILE_CLASS::BINARY }}, - { LOGFILE::BSE_SYSTEM_PARAMETERS, { "BSE_System_Parameters", BSE_SYSTEM_PARAMETERS_REC, "BSE_SYSPARMS", "BSE_SYSPARMS_REC", LOGFILE_CLASS::BINARY }}, - - { LOGFILE::SSE_DETAILED_OUTPUT, { "SSE_Detailed_Output", SSE_DETAILED_OUTPUT_REC, "SSE_DETAILED", "SSE_DETAILED_REC", LOGFILE_CLASS::STELLAR }}, - { LOGFILE::SSE_PULSAR_EVOLUTION, { "SSE_Pulsar_Evolution", SSE_PULSAR_EVOLUTION_REC, "SSE_PULSARS", "SSE_PULSARS_REC", LOGFILE_CLASS::STELLAR }}, - { LOGFILE::SSE_SUPERNOVAE, { "SSE_Supernovae", SSE_SUPERNOVAE_REC, "SSE_SNE", "SSE_SNE_REC", LOGFILE_CLASS::STELLAR }}, - { LOGFILE::SSE_SWITCH_LOG, { "SSE_Switch_Log", SSE_SWITCH_LOG_REC, "SSE_SWITCH_LOG", "SSE_SWITCH_REC", LOGFILE_CLASS::STELLAR }}, - { LOGFILE::SSE_SYSTEM_PARAMETERS, { "SSE_System_Parameters", SSE_SYSTEM_PARAMETERS_REC, "SSE_SYSPARMS", "SSE_SYSPARMS_REC", LOGFILE_CLASS::STELLAR }} + { LOGFILE::NONE, { "" , {}, "", "", LOGFILE_CLASS::NONE}}, + + { LOGFILE::DEBUG_LOG, { "Debug_Log", {}, "", "", LOGFILE_CLASS::NONE }}, + { LOGFILE::ERROR_LOG, { "Error_Log", {}, "", "", LOGFILE_CLASS::NONE }}, + + { LOGFILE::BSE_COMMON_ENVELOPES, { "BSE_Common_Envelopes", BSE_COMMON_ENVELOPES_REC, "BSE_CEE", "BSE_CEE_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_DETAILED_OUTPUT, { "BSE_Detailed_Output", BSE_DETAILED_OUTPUT_REC, "BSE_DETAILED", "BSE_DETAILED_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS, { "BSE_Double_Compact_Objects", BSE_DOUBLE_COMPACT_OBJECTS_REC, "BSE_DCO", "BSE_DCO_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_PULSAR_EVOLUTION, { "BSE_Pulsar_Evolution", BSE_PULSAR_EVOLUTION_REC, "BSE_PULSARS", "BSE_PULSARS_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_RLOF_PARAMETERS, { "BSE_RLOF", BSE_RLOF_PARAMETERS_REC, "BSE_RLOF", "BSE_RLOF_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_SUPERNOVAE, { "BSE_Supernovae", BSE_SUPERNOVAE_REC, "BSE_SNE", "BSE_SNE_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_SWITCH_LOG, { "BSE_Switch_Log", BSE_SWITCH_LOG_REC, "BSE_SWITCH_LOG", "BSE_SWITCH_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_SYSTEM_PARAMETERS, { "BSE_System_Parameters", BSE_SYSTEM_PARAMETERS_REC, "BSE_SYSPARMS", "BSE_SYSPARMS_REC", LOGFILE_CLASS::BINARY }}, + { LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG, { "BSE_System_Snapshot_Log", BSE_SYSTEM_SNAPSHOT_LOG_REC, "BSE_SYS_SNAPSHOT", "BSE_SYS_SNAPSHOT_REC", LOGFILE_CLASS::BINARY }}, + + { LOGFILE::SSE_DETAILED_OUTPUT, { "SSE_Detailed_Output", SSE_DETAILED_OUTPUT_REC, "SSE_DETAILED", "SSE_DETAILED_REC", LOGFILE_CLASS::STELLAR }}, + { LOGFILE::SSE_PULSAR_EVOLUTION, { "SSE_Pulsar_Evolution", SSE_PULSAR_EVOLUTION_REC, "SSE_PULSARS", "SSE_PULSARS_REC", LOGFILE_CLASS::STELLAR }}, + { LOGFILE::SSE_SUPERNOVAE, { "SSE_Supernovae", SSE_SUPERNOVAE_REC, "SSE_SNE", "SSE_SNE_REC", LOGFILE_CLASS::STELLAR }}, + { LOGFILE::SSE_SWITCH_LOG, { "SSE_Switch_Log", SSE_SWITCH_LOG_REC, "SSE_SWITCH_LOG", "SSE_SWITCH_REC", LOGFILE_CLASS::STELLAR }}, + { LOGFILE::SSE_SYSTEM_PARAMETERS, { "SSE_System_Parameters", SSE_SYSTEM_PARAMETERS_REC, "SSE_SYSPARMS", "SSE_SYSPARMS_REC", LOGFILE_CLASS::STELLAR }}, + { LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG, { "SSE_System_Snapshot_Log", SSE_SYSTEM_SNAPSHOT_LOG_REC, "SSE_SYS_SNAPSHOT", "SSE_SYS_SNAPSHOT_REC", LOGFILE_CLASS::STELLAR }} }; #endif // __LogTypedefs_h__ diff --git a/src/MS_gt_07.h b/src/MS_gt_07.h index c4085c995..e4bb2ffa4 100755 --- a/src/MS_gt_07.h +++ b/src/MS_gt_07.h @@ -48,10 +48,10 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0 && // ZAMS mass >= BRCEK_LOWER_MASS_LIMIT? m_Time <= 0.0) { // star not yet aged past creation? // yes - initialise - m_InitialMainSequenceCoreMass = MainSequence::CalculateInitialMainSequenceCoreMass(m_MZAMS); + m_InitialMainSequenceCoreMass = MainSequence::CalculateInitialMainSequenceCoreMass(m_MZAMS, m_InitialHeliumAbundance); m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; m_Luminosity = MainSequence::CalculateLuminosityOnPhase(m_Age, m_Mass0, m_LZAMS0); - m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); + m_Radius = MainSequence::CalculateRadiusOnPhase(m_Mass, m_Tau, m_RZAMS0); m_Temperature = BaseStar::CalculateTemperatureOnPhase_Static(m_Luminosity, m_Radius); } } @@ -60,7 +60,7 @@ class MS_gt_07: virtual public BaseStar, public MainSequence { // member functions - alphabetically double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const ; - double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 0.33; } // As coded in BSE. Using the inverse owing to how qCrit is defined in COMPAS. See Hurley et al. 2002 sect. 2.6.1 for additional details. + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_MS_GT_07; } double CalculateMassLossRateHurley(); double CalculateMassTransferRejuvenationFactor(); diff --git a/src/MS_lte_07.h b/src/MS_lte_07.h index b27cb576c..1bbe1ec8f 100755 --- a/src/MS_lte_07.h +++ b/src/MS_lte_07.h @@ -46,7 +46,7 @@ class MS_lte_07: virtual public BaseStar, public MainSequence { // member functions - alphabetically double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const ; - double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 1.44; } // From BSE. Using the inverse owing to how qCrit is defined in COMPAS. See Hurley et al. 2002 sect. 2.6.1 for additional details. + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_MS_LTE_07; } double CalculateMassTransferRejuvenationFactor() { return 1.0; } ENVELOPE DetermineEnvelopeType() const { return ENVELOPE::CONVECTIVE; } // Always CONVECTIVE diff --git a/src/MainSequence.cpp b/src/MainSequence.cpp index 3b7439adc..6315b0ae7 100644 --- a/src/MainSequence.cpp +++ b/src/MainSequence.cpp @@ -228,14 +228,14 @@ double MainSequence::CalculateGamma(const double p_Mass) const { #define B_GAMMA m_GammaConstants[static_cast(GAMMA_CONSTANTS::B_GAMMA)] // for convenience and readability - undefined at end of function #define C_GAMMA m_GammaConstants[static_cast(GAMMA_CONSTANTS::C_GAMMA)] // for convenience and readability - undefined at end of function - double gamma; +double gamma = 0.0; // default return value - if (utils::Compare(p_Mass, 1.0) <= 0) gamma = a[76] + (a[77] * PPOW(p_Mass - a[78], a[79])); - else if (utils::Compare(p_Mass, a[75]) <= 0) gamma = B_GAMMA + (a[80] - B_GAMMA) * PPOW((p_Mass - 1.0) / (a[75] - 1.0), a[81]); - else if (utils::Compare(p_Mass, (a[75] + 0.1)) <= 0) gamma = C_GAMMA - (10.0 * (p_Mass - a[75]) * C_GAMMA); // included = case, missing from Hurley+ 2000 - else gamma = 0.0; // this really is zero + if (utils::Compare(p_Mass, 1.0) <= 0) gamma = a[76] + (a[77] * PPOW(std::abs(p_Mass - a[78]), a[79])); // BSE Fortran code has abs() +else if (utils::Compare(p_Mass, a[75]) <= 0) gamma = B_GAMMA + (a[80] - B_GAMMA) * PPOW((p_Mass - 1.0) / (a[75] - 1.0), a[81]); +else if (utils::Compare(p_Mass, (a[75] + 0.1)) <= 0) gamma = C_GAMMA - (10.0 * (p_Mass - a[75]) * C_GAMMA); // see discussion just prior to eq 23 - the end point is wrong in the arxiv version of Hurley et al. 2000 (should be 0.1, not 1.0) - confirmed in BSE Fortran code +else gamma = 0.0; // see discussion just prior to eq 23 - confirmed in BSE Fortran code - return gamma; +return std::max(0.0, gamma); // see discussion following eq 23 - confirmed in BSE Fortran code #undef C_GAMMA #undef B_GAMMA @@ -289,10 +289,10 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl #define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function // If BRCEK core prescription is used, return luminosity from Shikauchi et al. (2024) during core hydrogen burning (valid for MZAMS >= 15 Msol) or - // luminosity that smoothly connects MS and HG during MS hook (valid for MZAMS >= BRCEK_LOWER_MASS_LIMIT) - if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::BRCEK) && (utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0)) { - double tMS = timescales(tMS); - if (utils::Compare(p_Time, 0.99 * tMS) > 0) // star in MS hook? + // luminosity that smoothly connects MS and HG during MS hook (valid for MZAMS >= BRCEK_LOWER_MASS_LIMIT); do not use Shikauchi luminosity + // prescription during CHE + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::BRCEK) && (utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0) && !m_CHE) { + if (utils::Compare(p_Time, 0.99 * timescales(tMS)) > 0) // star in MS hook? return CalculateLuminosityTransitionToHG(p_Mass, p_Time, p_LZAMS); else { if (utils::Compare(m_MZAMS, 15.0) >= 0) // use Shikauchi luminosity if MZAMS >= 15 Msun @@ -340,9 +340,16 @@ double MainSequence::CalculateLuminosityOnPhase(const double p_Time, const doubl */ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const { DBL_VECTOR L_COEFFICIENTS = std::get<2>(SHIKAUCHI_COEFFICIENTS); - double logMixingCoreMass = std::log10(p_CoreMass); - double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * p_HeliumAbundanceCore + L_COEFFICIENTS[2] * logMixingCoreMass * p_HeliumAbundanceCore + L_COEFFICIENTS[3] * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[4] * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[5] * logMixingCoreMass * logMixingCoreMass * logMixingCoreMass + L_COEFFICIENTS[6] * p_HeliumAbundanceCore * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[7] * logMixingCoreMass * logMixingCoreMass * p_HeliumAbundanceCore + L_COEFFICIENTS[8] * logMixingCoreMass * p_HeliumAbundanceCore * p_HeliumAbundanceCore + L_COEFFICIENTS[9]; + // common factors + double logMixingCoreMass = std::log10(p_CoreMass); + double logMixingCoreMass_2 = logMixingCoreMass * logMixingCoreMass; + double logMixingCoreMass_3 = logMixingCoreMass_2 * logMixingCoreMass; + + double heliumAbundanceCore_2 = p_HeliumAbundanceCore * p_HeliumAbundanceCore; + double heliumAbundanceCore_3 = heliumAbundanceCore_2 * p_HeliumAbundanceCore; + + double logL = L_COEFFICIENTS[0] * logMixingCoreMass + L_COEFFICIENTS[1] * p_HeliumAbundanceCore + L_COEFFICIENTS[2] * logMixingCoreMass * p_HeliumAbundanceCore + L_COEFFICIENTS[3] * logMixingCoreMass_2 + L_COEFFICIENTS[4] * heliumAbundanceCore_2 + L_COEFFICIENTS[5] * logMixingCoreMass_3 + L_COEFFICIENTS[6] * heliumAbundanceCore_3 + L_COEFFICIENTS[7] * logMixingCoreMass_2 * p_HeliumAbundanceCore + L_COEFFICIENTS[8] * logMixingCoreMass * heliumAbundanceCore_2 + L_COEFFICIENTS[9] * logMixingCoreMass_3 * logMixingCoreMass + L_COEFFICIENTS[10] * heliumAbundanceCore_3 * p_HeliumAbundanceCore + L_COEFFICIENTS[11] * logMixingCoreMass * heliumAbundanceCore_3 + L_COEFFICIENTS[12] * logMixingCoreMass_2 * heliumAbundanceCore_2 + L_COEFFICIENTS[13] * logMixingCoreMass_3 * p_HeliumAbundanceCore + L_COEFFICIENTS[14]; return PPOW(10.0, logL); } @@ -363,12 +370,13 @@ double MainSequence::CalculateLuminosityShikauchi(const double p_CoreMass, const * @return Luminosity on the Main Sequence (for age between tHook and tMS) */ double MainSequence::CalculateLuminosityTransitionToHG(const double p_Mass, const double p_Age, double const p_LZAMS) const { +#define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function + HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); double luminosityTAMS = clone->Luminosity(); // Get luminosity from clone (with updated Mass0) delete clone; clone = nullptr; // Return the memory allocated for the clone - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - double ageAtHookStart = 0.99 * tMS; + double ageAtHookStart = 0.99 * timescales(tMS); double luminosityAtHookStart; if (utils::Compare(m_MZAMS, std::max(15.0, BRCEK_LOWER_MASS_LIMIT)) >= 0) @@ -376,7 +384,10 @@ double MainSequence::CalculateLuminosityTransitionToHG(const double p_Mass, cons else luminosityAtHookStart = CalculateLuminosityOnPhase(ageAtHookStart, p_Mass, p_LZAMS); // Do not use Shikauchi luminosity for MZAMS < 15 Msun - return (luminosityAtHookStart * (tMS - p_Age) + luminosityTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); // Linear interpolation + // Linear interpolation + return (luminosityAtHookStart * (timescales(tMS) - p_Age) + luminosityTAMS * (p_Age - ageAtHookStart)) / (timescales(tMS) - ageAtHookStart); + +#undef timescales } @@ -537,81 +548,30 @@ double MainSequence::CalculateRadiusAtPhaseEnd(const double p_Mass, const double * Hurley et al. 2000, eq 13 * * - * double CalculateRadiusOnPhase(const double p_Mass, const double p_Time, const double p_RZAMS) + * double CalculateRadiusOnPhase(const double p_Mass, const double p_Tau, const double p_RZAMS) * * @param [IN] p_Mass Mass in Msol - * @param [IN] p_Time Time (after ZAMS) in Myr + * @param [IN] p_Tau Fractional age on Main Sequence * @param [IN] p_RZAMS Zero Age Main Sequence (ZAMS) Radius * @return Radius on the Main Sequence in Rsol */ -double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_Time, const double p_RZAMS) const { +double MainSequence::CalculateRadiusOnPhase(const double p_Mass, const double p_Tau, const double p_RZAMS) const { #define a m_AnCoefficients // for convenience and readability - undefined at end of function -#define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function - + // If BRCEK core prescription is used, return radius that smoothly connects the beginning of MS hook and the beginning of HG, // valid for stars with MZAMS >= BRCEK_LOWER_MASS_LIMIT if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::BRCEK) && (utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0)) { - double tMS = timescales(tMS); - if (utils::Compare(p_Time, 0.99 * tMS) > 0) // star in MS hook? - return CalculateRadiusTransitionToHG(p_Mass, p_Time, p_RZAMS); + if (utils::Compare(p_Tau, 0.99) > 0) // star in MS hook? + return CalculateRadiusTransitionToHG(p_Mass, p_Tau, p_RZAMS); } - - const double epsilon = 0.01; - - double RTMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); - double alphaR = CalculateAlphaR(p_Mass); - double betaR = CalculateBetaR(p_Mass); - double deltaR = CalculateDeltaR(p_Mass); - double gamma = CalculateGamma(p_Mass); - - double mu = std::max(0.5, (1.0 - (0.01 * std::max((a[6] / PPOW(p_Mass, a[7])), (a[8] + (a[9] / PPOW(p_Mass, a[10]))))))); // Hurley et al. 2000, eq 7 - double tHook = mu * timescales(tBGB); // Hurley et al. 2000, just after eq 5 - double tau = p_Time / timescales(tMS); // Hurley et al. 2000, eq 11 - double tau1 = std::min(1.0, (p_Time / tHook)); // Hurley et al. 2000, eq 14 - double tau2 = std::max(0.0, std::min(1.0, (p_Time - ((1.0 - epsilon) * tHook)) / (epsilon * tHook))); // Hurley et al. 2000, eq 15 - - // pow() is slow - use multiplication where it makes sense - double tau_3 = tau * tau * tau; - double tau_10 = tau < FLOAT_TOLERANCE_ABSOLUTE ? 0.0: tau_3 * tau_3 * tau_3 * tau; // direct comparison, to avoid underflow - double tau_40 = tau_10 < FLOAT_TOLERANCE_ABSOLUTE ? 0.0: tau_10 * tau_10 * tau_10 * tau_10; // direct comparison, to avoid underflow - double tau1_3 = tau1 * tau1 * tau1; - double tau2_3 = tau2 * tau2 * tau2; - - double logRMS_RZAMS = alphaR * tau; // Hurley et al. 2000, eq 13, part 1 - logRMS_RZAMS += betaR * tau_10; // Hurley et al. 2000, eq 13, part 2 - logRMS_RZAMS += gamma * tau_40; // Hurley et al. 2000, eq 13, part 3 - logRMS_RZAMS += (log10(RTMS / p_RZAMS) - alphaR - betaR - gamma) * tau_3; // Hurley et al. 2000, eq 13, part 4 - logRMS_RZAMS -= deltaR * (tau1_3 - tau2_3); // Hurley et al. 2000, eq 13, part 5 - - return p_RZAMS * PPOW(10.0, logRMS_RZAMS); // rewrite Hurley et al. 2000, eq 13 for R(t) - -#undef timescales -#undef a -} - - -/* - * Calculate radius on the Main Sequence - * - * Hurley et al. 2000, eq 13 - * - * - * double CalculateRadiusOnPhaseTau(const double p_Mass, const double p_Tau) - * - * @param [IN] p_Mass Mass in Msol - * @param [IN] p_Tau Fractional age on Main Sequence - * @return Radius on the Main Sequence in Rsol - */ -double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double p_Tau) const { -#define a m_AnCoefficients // for convenience and readability - undefined at end of function - + double radius = m_Radius; + const double epsilon = 0.01; double tBGB = CalculateLifetimeToBGB(p_Mass); double tMS = CalculateLifetimeOnPhase(p_Mass, tBGB); - double RZAMS = CalculateRadiusAtZAMS(p_Mass); - double RTMS = CalculateRadiusAtPhaseEnd(p_Mass, RZAMS); + double RTMS = CalculateRadiusAtPhaseEnd(p_Mass, p_RZAMS); double alphaR = CalculateAlphaR(p_Mass); double betaR = CalculateBetaR(p_Mass); double deltaR = CalculateDeltaR(p_Mass); @@ -623,20 +583,36 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double double tau1 = std::min(1.0, (time / tHook)); // ibid, eq 14 double tau2 = std::max(0.0, std::min(1.0, (time - ((1.0 - epsilon) * tHook)) / (epsilon * tHook))); // ibid, eq 15 - // pow() is slow - use multipliaction where it makes sense + // pow() is slow - use multiplication where it makes sense double tau_3 = p_Tau * p_Tau * p_Tau; - double tau_10 = tau_3 * tau_3 * tau_3 * p_Tau; - double tau_40 = tau_10 * tau_10 * tau_10 * tau_10; + double tau_10 = p_Tau < FLOAT_TOLERANCE_ABSOLUTE ? 0.0: tau_3 * tau_3 * tau_3 * p_Tau; // direct comparison, to avoid underflow + double tau_40 = tau_10 < FLOAT_TOLERANCE_ABSOLUTE ? 0.0: tau_10 * tau_10 * tau_10 * tau_10; // direct comparison, to avoid underflow double tau1_3 = tau1 * tau1 * tau1; double tau2_3 = tau2 * tau2 * tau2; double logRMS_RZAMS = alphaR * p_Tau; // ibid, eq 13, part 1 logRMS_RZAMS += betaR * tau_10; // ibid, eq 13, part 2 logRMS_RZAMS += gamma * tau_40; // ibid, eq 13, part 3 - logRMS_RZAMS += (log10(RTMS / RZAMS) - alphaR - betaR - gamma) * tau_3; // ibid, eq 13, part 4 + logRMS_RZAMS += (log10(RTMS / p_RZAMS) - alphaR - betaR - gamma) * tau_3; // ibid, eq 13, part 4 logRMS_RZAMS -= deltaR * (tau1_3 - tau2_3); // ibid, eq 13, part 5 - return RZAMS * PPOW(10.0, logRMS_RZAMS); // rewrite Hurley et al. 2000, eq 13 for R(t) + radius = p_RZAMS * PPOW(10.0, logRMS_RZAMS); // rewrite Hurley et al. 2000, eq 13 for R(t) + + // If BRCEK prescription is used and star was stripped below its initial core mass, radius needs to be adjusted + if (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::BRCEK && utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0) { + + double heliumAbundanceSurface = m_HeliumAbundanceSurface; + if (utils::Compare(p_Mass, m_InitialMainSequenceCoreMass) < 0) + // By tracing the helium profile in the star, calculate how the surface helium abundance changes if mass drops below the initial core mass + heliumAbundanceSurface = m_HeliumAbundanceCoreOut + (p_Mass - m_MainSequenceCoreMass) * (m_HeliumAbundanceSurface - m_HeliumAbundanceCoreOut) / (m_InitialMainSequenceCoreMass - m_MainSequenceCoreMass); + + // Factor that scales radius based on surface helium abundance + double surfaceAbundanceFactor = (utils::Compare(m_HeliumAbundanceCore, m_InitialHeliumAbundance) != 0) ? (heliumAbundanceSurface - m_InitialHeliumAbundance) / (m_HeliumAbundanceCore - m_InitialHeliumAbundance) : 0.0; + + radius = radius + (p_RZAMS - radius) * surfaceAbundanceFactor; + } + + return radius; #undef a } @@ -645,28 +621,27 @@ double MainSequence::CalculateRadiusOnPhaseTau(const double p_Mass, const double /* * Calculate radius on the transition from the Main Sequence to the HG when BRCEK core mass prescription is used * - * Core mass prescription from Shikauchi et al. (2024) cannot be used beyond the MS hook (beyond age 0.99 * tMS), and this + * Core mass prescription from Shikauchi et al. (2024) cannot be used beyond the MS hook (beyond Tau = 0.99), and this * function smoothly connects the radius between the beginning of the hook and the beginning of the HG * * - * double CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) + * double CalculateRadiusTransitionToHG(const double p_Mass, const double p_Tau) * @param [IN] p_Mass Mass in Msol - * @param [IN] p_Age Age in Myr + * @param [IN] p_Tau Fractional age on Main Sequence * @param [IN] p_RZAMS Zero Age Main Sequence (ZAMS) Radius - * @return Radius on the Main Sequence (for age between tHook and tMS) + * @return Radius on the Main Sequence (for Tau between 0.99 and 1) */ -double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const { +double MainSequence::CalculateRadiusTransitionToHG(const double p_Mass, const double p_Tau, const double p_RZAMS) const { HG *clone = HG::Clone(static_cast(const_cast(*this)), OBJECT_PERSISTENCE::EPHEMERAL); - double radiusTAMS = clone->Radius(); // Get radius from clone (with updated Mass0) + // Select radius at TAMS from the HG clone or current radius (whichever is smaller), relevant for stars that were significantly + // stripped as this prevents radius expansion during the hook, and delays possible mass transfer to the start of HG + double radiusTAMS = std::min(clone->Radius(), m_Radius); // Get radius from clone (with updated Mass0) delete clone; clone = nullptr; // Return the memory allocated for the clone - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - double tauAtHookStart = 0.99; - double ageAtHookStart = tauAtHookStart * tMS; - double radiusAtHookStart = CalculateRadiusOnPhaseTau(p_Mass, tauAtHookStart); + double radiusAtHookStart = CalculateRadiusOnPhase(p_Mass, 0.99, p_RZAMS); // Hook starts at Tau = 0.99 - return (radiusAtHookStart * (tMS - p_Age) + radiusTAMS * (p_Age - ageAtHookStart)) / (tMS - ageAtHookStart); // Linear interpolation + return (radiusAtHookStart * (1.0 - p_Tau) + radiusTAMS * (p_Tau - 0.99)) / 0.01; // Linear interpolation } @@ -778,7 +753,8 @@ DBL_DBL MainSequence::CalculateConvectiveEnvelopeMass() const { * mass either through winds or case A mass transfer according to Shikauchi et al. (2024) * * This function also accounts for mass gain by modeling rejuvenation and updates the initial mixing core mass - * and the helium abundance just outside the core + * and the helium abundance just outside the core. If star undergoes significant stripping and the total mass + * reaches the initial convective core mass, surface helium and hydrogen abundances are also updated * * DBL_DBL CalculateMainSequenceCoreMassBrcek(const double p_Dt, const double p_MassLossRate) * @@ -795,8 +771,13 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassBrcek(const double p_Dt, cons auto fmix = [&](double mass) { return FMIX_COEFFICIENTS[0] + FMIX_COEFFICIENTS[1] * std::exp(-mass / FMIX_COEFFICIENTS[2]); }; // Shikauchi et al. (2024), eq (A3) double alpha = PPOW(10.0, std::max(-2.0, ALPHA_COEFFICIENTS[1] * m_MainSequenceCoreMass + ALPHA_COEFFICIENTS[2])) + ALPHA_COEFFICIENTS[0]; // ibid, eq (A2) - double g = -0.0044 * m_MZAMS + 0.27; // ibid, eq (A7) - double delta = std::min(PPOW(10.0, -(m_HeliumAbundanceCore - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity) + g), 1.0); // ibid, eq (A6) + double g = SHIKAUCHI_DELTA_COEFFICIENTS[1] * m_MainSequenceCoreMass + SHIKAUCHI_DELTA_COEFFICIENTS[2]; // ibid, eq (A7) + + double delta; + if (p_MassLossRate <= 0.0) + delta = std::min(PPOW(10.0, -SHIKAUCHI_DELTA_COEFFICIENTS[0] * (m_HeliumAbundanceCore - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity) + g), 1.0); // ibid, eq (A6) + else + delta = PPOW(2.0, -(m_HeliumAbundanceCore - m_InitialHeliumAbundance) / (1.0 - m_InitialHeliumAbundance - m_Metallicity)); // updated prescription for mass gain double deltaYc = CalculateLuminosityOnPhase() / (Q_CNO * m_MainSequenceCoreMass) * p_Dt; // Change in central helium fraction; ibid, eq (12) double deltaMass = p_MassLossRate * p_Dt * MYR_TO_YEAR; // Total mass lost/gained @@ -804,8 +785,8 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassBrcek(const double p_Dt, cons double deltaCoreMassNatural = -alpha / (1 - alpha * m_HeliumAbundanceCore) * deltaYc * m_MainSequenceCoreMass; // Change in core mass due to natural decay; ibid, eq (4) double deltaCoreMass = deltaCoreMassNatural + deltaCoreMassML; // Total difference in core mass - double newMixingCoreMass = m_MainSequenceCoreMass + deltaCoreMass; // New mixing core mass - double newCentralHeliumFraction = m_HeliumAbundanceCore + deltaYc; // New central helium fraction + double newMixingCoreMass = std::min(m_MainSequenceCoreMass + deltaCoreMass, BRCEK_CORE_MASS_TO_MASS_RATIO_LIMIT * (m_Mass + deltaMass)); // New mixing core mass, always has to be smaller than the total mass + double newCentralHeliumFraction = std::min(m_HeliumAbundanceCore + deltaYc, 1.0 - m_Metallicity); // New central helium fraction, capped at 1-Z if (deltaCoreMass > 0.0) { // If the core grows, we need to account for rejuvenation if (utils::Compare(newMixingCoreMass, m_InitialMainSequenceCoreMass) < 0) { // New core mass less than initial core mass? @@ -830,26 +811,46 @@ DBL_DBL MainSequence::CalculateMainSequenceCoreMassBrcek(const double p_Dt, cons m_InitialMainSequenceCoreMass = newMixingCoreMass; } } - else - m_HeliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c + else { // Core decayed + // If total mass dropped below the initial core mass, partially processed material is exposed and surface abundance needs to be adjusted + if (utils::Compare(m_Mass + deltaMass, m_InitialMainSequenceCoreMass) < 0) { + // Set surface helium abundance following the helium abundance profile in the star + m_HeliumAbundanceSurface = m_HeliumAbundanceCoreOut + (m_Mass + deltaMass - m_MainSequenceCoreMass) * (m_HeliumAbundanceSurface - m_HeliumAbundanceCoreOut) / (m_InitialMainSequenceCoreMass - m_MainSequenceCoreMass); + m_HydrogenAbundanceSurface = 1.0 - m_Metallicity - m_HeliumAbundanceSurface; + m_InitialMainSequenceCoreMass = m_Mass + deltaMass; // Update the initial core mass + } + m_HeliumAbundanceCoreOut = newCentralHeliumFraction; // If core did not grow, Y_out = Y_c + } return std::tuple (newMixingCoreMass, std::min(newCentralHeliumFraction, 1.0 - m_Metallicity)); } /* - * Calculate the initial convective core mass of a main sequence star using Equation (A3) from Shikauchi et al. (2024), - * also used for calculating core mass after MS merger + * Calculate the initial convective core mass of a main sequence star at ZAMS using Equation (A3) from Shikauchi+ (2024), + * or after full mixing (due to merger or CHE) for an arbitrary central helium fraction using the approach + * described in Brcek+ (2025) * - * double CalculateInitialMainSequenceCoreMass(const double p_MZAMS) + * double CalculateInitialMainSequenceCoreMass(const double p_Mass, const double p_HeliumAbundanceCore) * - * @param [IN] p_MZAMS Mass at ZAMS or after merger in Msol + * @param [IN] p_Mass Mass at ZAMS, after merger or after spin down of CH star in Msol + * @param [IN] p_HeliumAbundanceCore Central helium fraction * @return Mass of the convective core at ZAMS or after merger in Msol */ -double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_MZAMS) const { - DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); - double fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_MZAMS / fmixCoefficients[2]); - return fmix * p_MZAMS; +double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_Mass, const double p_HeliumAbundanceCore) const { + + double fmix = 0.0; + // At ZAMS, use the approach from Shikauchi+ (2024) + if (utils::Compare(p_HeliumAbundanceCore, m_InitialHeliumAbundance) == 0) { + DBL_VECTOR fmixCoefficients = std::get<1>(SHIKAUCHI_COEFFICIENTS); + fmix = fmixCoefficients[0] + fmixCoefficients[1] * std::exp(-p_Mass / fmixCoefficients[2]); + } + // After full mixing not at ZAMS, use the approach from Brcek+ (2025) + else { + double h = PPOW(10.0, p_HeliumAbundanceCore * (p_HeliumAbundanceCore + 2.0) / 4.0); + fmix = (BRCEK_FMIX_COEFFICIENTS[0] + BRCEK_FMIX_COEFFICIENTS[1] * std::exp(-p_Mass * h / BRCEK_FMIX_COEFFICIENTS[2])) * PPOW(1.0 - BRCEK_FMIX_COEFFICIENTS[4] / (p_Mass * h), BRCEK_FMIX_COEFFICIENTS[3]); + } + return fmix * p_Mass; } @@ -864,13 +865,15 @@ double MainSequence::CalculateInitialMainSequenceCoreMass(const double p_MZAMS) * @param [IN] p_MassLossRate Mass loss rate either from stellar winds or mass transfer in Msol yr-1 */ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_MassLossRate) { +#define timescales(x) m_Timescales[static_cast(TIMESCALE::x)] // for convenience and readability - undefined at end of function double mainSequenceCoreMass = m_MainSequenceCoreMass; // default is no change double heliumAbundanceCore = m_HeliumAbundanceCore; // default is no change double age = m_Age; // default is no change switch (OPTIONS->MainSequenceCoreMassPrescription()) { - case CORE_MASS_PRESCRIPTION::ZERO: + case CORE_MASS_PRESCRIPTION::HURLEY: + // In the Hurley et al. (2000) formalism, MS stars do not have a distinct core and core evolution is not tracked mainSequenceCoreMass = 0.0; break; @@ -885,15 +888,20 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ // Set core mass following Shikauchi et al. (2024) and account for rejuvenation if core grows // MZAMS >= BRCEK_LOWER_MASS_LIMIT? BRCEK prescription valid if (utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0) { - // Only proceed with calculations if star is not in MS hook (Yc < 1-Z), time step is not zero, - // and when the mass loss rate argument is equal to the total mass loss rate - // (i.e. total mass loss rate was updated, this prevents the calculation in SSE if it was executed as part of BSE for the same time step) - if ((utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) < 0) && (utils::Compare(p_Dt, 0.0) != 0) && (utils::Compare(p_MassLossRate, m_TotalMassLossRate) == 0)) { - - std::tie(mainSequenceCoreMass, heliumAbundanceCore) = CalculateMainSequenceCoreMassBrcek(p_Dt, p_MassLossRate); // calculate and update the core mass and central helium fraction - - double tMS = m_Timescales[static_cast(TIMESCALE::tMS)]; - age = (heliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * tMS; // update the effective age based on central helium fraction + // Only proceed with calculations if star is not in MS hook (Yc < 1-Z) and time step is not zero + if ((utils::Compare(m_HeliumAbundanceCore, 1.0 - m_Metallicity) < 0) && (utils::Compare(p_Dt, 0.0) != 0)) { + // Update the core mass and central helium fraction only if the mass loss rate argument is equal to the total mass loss rate + // (i.e. total mass loss rate was updated, this prevents the calculation in SSE if it was executed as part of BSE for the same time step) + if (utils::Compare(p_MassLossRate, m_TotalMassLossRate) == 0) { + // Calculate and update the core mass and central helium fraction + std::tie(mainSequenceCoreMass, heliumAbundanceCore) = CalculateMainSequenceCoreMassBrcek(p_Dt, p_MassLossRate); + // Update effective age here only if core hydrogen was exhausted + age = heliumAbundanceCore == 1.0 - m_Metallicity ? 0.99 * timescales(tMS) : age; + } + // Update effective age only when stars are aged in SSE (when p_MassLossRate = -Mdot) + if (utils::Compare(p_MassLossRate, -m_Mdot) == 0) + // Update the effective age based on central helium fraction + age = (heliumAbundanceCore - m_InitialHeliumAbundance) / m_InitialHydrogenAbundance * 0.99 * timescales(tMS); } } // MZAMS < BRCEK_LOWER_MASS_LIMIT? MANDEL prescription used @@ -910,6 +918,8 @@ void MainSequence::UpdateMainSequenceCoreMass(const double p_Dt, const double p_ m_MainSequenceCoreMass = mainSequenceCoreMass; // update core mass m_HeliumAbundanceCore = heliumAbundanceCore; // update core helium abundance m_Age = age; // update age + +#undef timescales } @@ -958,7 +968,7 @@ double MainSequence::CalculateLifetimeOnPhase(const double p_Mass, const double double tHook = mu * p_TBGB; // For mass < Mhook, x > mu (i.e. for stars without a hook) - double x = std::max(0.95, std::min((0.95 - (0.03 * (LogMetallicityXi() + 0.30103))), 0.99)); + double x = std::max(0.95, std::min((0.95 - (0.03 * (LogMetallicityXiHurley() + 0.30103))), 0.99)); return std::max(tHook, (x * p_TBGB)); @@ -1146,12 +1156,14 @@ void MainSequence::UpdateAfterMerger(double p_Mass, double p_HydrogenMass) { m_Age = m_Tau * timescales(tMS); m_HeliumAbundanceCore = 1.0 - m_Metallicity - p_HydrogenMass / p_Mass; - m_HydrogenAbundanceCore = 1.0 - m_Metallicity - m_HeliumAbundanceCore; + m_HeliumAbundanceSurface = m_HeliumAbundanceCore; // abundances are the same throughout the star, assuming uniform mixing after merger + m_HydrogenAbundanceSurface = m_HydrogenAbundanceCore; + if ((OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::BRCEK) && (utils::Compare(m_MZAMS, BRCEK_LOWER_MASS_LIMIT) >= 0)) { - m_InitialMainSequenceCoreMass = CalculateInitialMainSequenceCoreMass(p_Mass); // update initial mixing core mass - m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; // update core mass + m_InitialMainSequenceCoreMass = CalculateInitialMainSequenceCoreMass(p_Mass, m_HeliumAbundanceCore); // update initial mixing core mass + m_MainSequenceCoreMass = m_InitialMainSequenceCoreMass; // update core mass } UpdateAttributesAndAgeOneTimestep(0.0, 0.0, 0.0, true); @@ -1315,8 +1327,8 @@ double MainSequence::InterpolateGeEtAlQCrit(const QCRIT_PRESCRIPTION p_qCritPres double interpolatedQCritForZ = p_massTransferEfficiencyBeta * interpolatedQCritUpperEff + (1.0 - p_massTransferEfficiencyBeta) * interpolatedQCritLowerEff; // Don't need to use nearest neighbor for this, beta is always between 0 and 1 qCritPerMetallicity[ii] = interpolatedQCritForZ; } - double logZlo = -3; // log10(0.001) - double logZhi = LOG10_ZSOL; // log10(0.02) + double logZlo = -3; // log10(0.001) + double logZhi = LOG10_ZSOL_HURLEY; // log10(0.02) return qCritPerMetallicity[1] + (m_Log10Metallicity - logZhi)*(qCritPerMetallicity[1] - qCritPerMetallicity[0])/(logZhi - logZlo); } @@ -1334,7 +1346,7 @@ std::tuple MainSequence::InterpolateShikauc DBL_VECTOR alphaCoeff(3, 0.0); DBL_VECTOR fmixCoeff(3, 0.0); - DBL_VECTOR lCoeff(10, 0.0); + DBL_VECTOR lCoeff(15, 0.0); // Skip calculation if BRCEK core prescription is not used if (OPTIONS->MainSequenceCoreMassPrescription() != CORE_MASS_PRESCRIPTION::BRCEK) @@ -1343,9 +1355,9 @@ std::tuple MainSequence::InterpolateShikauc double logZ = std::log10(p_Metallicity); // Coefficients are given for these metallicities - double low = std::log10(0.1 * ZSOL_ASPLUND); - double middle = std::log10(1.0 / 3.0 * ZSOL_ASPLUND); - double high = std::log10(ZSOL_ASPLUND); + double low = std::log10(0.1 * ZSOL_HURLEY); + double middle = std::log10(1.0 / 3.0 * ZSOL_HURLEY); + double high = std::log10(ZSOL_HURLEY); // common factors double middle_logZ = middle - logZ; @@ -1367,7 +1379,7 @@ std::tuple MainSequence::InterpolateShikauc alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[0][i] * middle_logZ + SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * logZ_low) / middle_low; fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[0][i] * middle_logZ + SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * logZ_low) / middle_low; } - for (size_t i = 0; i < 10; i++) + for (size_t i = 0; i < 15; i++) lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[0][i] * middle_logZ + SHIKAUCHI_L_COEFFICIENTS[1][i] * logZ_low) / middle_low; } // Linear interpolation between metallicity middle and high @@ -1376,7 +1388,7 @@ std::tuple MainSequence::InterpolateShikauc alphaCoeff[i] = (SHIKAUCHI_ALPHA_COEFFICIENTS[1][i] * high_logZ + SHIKAUCHI_ALPHA_COEFFICIENTS[2][i] * logZ_middle) / high_middle; fmixCoeff[i] = (SHIKAUCHI_FMIX_COEFFICIENTS[1][i] * high_logZ + SHIKAUCHI_FMIX_COEFFICIENTS[2][i] * logZ_middle) / high_middle; } - for (size_t i = 0; i < 10; i++) + for (size_t i = 0; i < 15; i++) lCoeff[i] = (SHIKAUCHI_L_COEFFICIENTS[1][i] * high_logZ + SHIKAUCHI_L_COEFFICIENTS[2][i] * logZ_middle) / high_middle; } // Linear extrapolation (constant) for metallicity equal to solar or higher diff --git a/src/MainSequence.h b/src/MainSequence.h index f6fd5c49c..d378b1559 100644 --- a/src/MainSequence.h +++ b/src/MainSequence.h @@ -25,9 +25,7 @@ class MainSequence: virtual public BaseStar { protected: // member variables - double m_HeliumAbundanceCoreOut = m_InitialHeliumAbundance; // Helium abundance just outside the core, used for rejuvenation calculations - double m_InitialMainSequenceCoreMass = 0.0; // Initial mass of the mixing core is initialised in MS_gt_07 class // member functions - alphabetically double CalculateAlphaL(const double p_Mass) const; @@ -49,7 +47,7 @@ class MainSequence: virtual public BaseStar { double CalculateCOCoreMassAtPhaseEnd() const { return CalculateCOCoreMassOnPhase(); } // Same as on phase double CalculateCOCoreMassOnPhase() const { return 0.0; } // McCO(MS) = 0.0 - double CalculateCoreMassAtPhaseEnd() const { return (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::MANDEL) ? MainSequenceCoreMass() : 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer + double CalculateCoreMassAtPhaseEnd() const { return (OPTIONS->MainSequenceCoreMassPrescription() == CORE_MASS_PRESCRIPTION::MANDEL) ? std::min(MainSequenceCoreMass(), m_Mass) : 0.0; } // Accounts for minimal core mass built up prior to mass loss through mass transfer; core mass can't exceed total mass double CalculateCoreMassOnPhase() const { return 0.0; } // Mc(MS) = 0.0 (Hurley et al. 2000, just before eq 28) double CalculateHeCoreMassAtPhaseEnd() const { return CalculateCoreMassAtPhaseEnd(); } // Same as He core mass @@ -60,14 +58,14 @@ class MainSequence: virtual public BaseStar { double CalculateHeliumAbundanceCoreOnPhase() const { return CalculateHeliumAbundanceCoreOnPhase(m_Tau); } // Use class member variables double CalculateHeliumAbundanceSurfaceAtPhaseEnd() const { return CalculateHeliumAbundanceSurfaceOnPhase(); } - double CalculateHeliumAbundanceSurfaceOnPhase() const { return m_InitialHeliumAbundance; } // Use class member variables + double CalculateHeliumAbundanceSurfaceOnPhase() const { return m_HeliumAbundanceSurface; } // Use class member variables double CalculateHydrogenAbundanceCoreAtPhaseEnd() const { return CalculateHydrogenAbundanceCoreOnPhase(); } double CalculateHydrogenAbundanceCoreOnPhase(const double p_Tau) const; double CalculateHydrogenAbundanceCoreOnPhase() const { return CalculateHydrogenAbundanceCoreOnPhase(m_Tau); } // Use class member variables double CalculateHydrogenAbundanceSurfaceAtPhaseEnd() const { return CalculateHydrogenAbundanceSurfaceOnPhase(); } - double CalculateHydrogenAbundanceSurfaceOnPhase() const { return m_InitialHydrogenAbundance; } // Use class member variables + double CalculateHydrogenAbundanceSurfaceOnPhase() const { return m_HydrogenAbundanceSurface; } // Use class member variables double CalculateLifetimeOnPhase(const double p_Mass, const double p_TBGB) const; @@ -78,23 +76,22 @@ class MainSequence: virtual public BaseStar { double CalculateLuminosityShikauchi(const double p_CoreMass, const double p_HeliumAbundanceCore) const; double CalculateLuminosityTransitionToHG(const double p_Mass, const double p_Age, double const p_LZAMS) const; DBL_DBL CalculateMainSequenceCoreMassBrcek(const double p_Dt, const double p_MassLossRate); - double CalculateInitialMainSequenceCoreMass(const double p_MZAMS) const; + double CalculateInitialMainSequenceCoreMass(const double p_Mass, const double p_HeliumAbundanceCore) const; double CalculateMomentOfInertia() const { return (0.1 * (m_Mass) * m_Radius * m_Radius); } // k2 = 0.1 as defined in Hurley et al. 2000, after eq 109 double CalculatePerturbationMu() const { return 5.0; } // mu(MS) = 5.0 (Hurley et al. 2000, eqs 97 & 98) double CalculateRadialExtentConvectiveEnvelope() const; - double CalculateRadiusOnMassChange(double p_dM) { return CalculateRadiusOnPhaseTau(m_Mass + p_dM, m_Tau); } + double CalculateRadiusOnMassChange(double p_dM) { return CalculateRadiusOnPhase(m_Mass + p_dM, m_Tau, CalculateRadiusAtZAMS(m_Mass + p_dM)); } - double CalculateRadiusOnPhaseTau(const double p_Mass, const double p_Tau) const; + double CalculateRadiusOnPhase(const double p_Mass, const double p_Tau, const double p_RZAMS) const; - double CalculateRadiusOnPhase(const double p_Mass, const double p_Time, const double p_RZAMS) const; double CalculateRadiusAtPhaseEnd(const double p_Mass, const double p_RZAMS) const; double CalculateRadiusAtPhaseEnd() const { return CalculateRadiusAtPhaseEnd(m_Mass, m_RZAMS); } // Use class member variables - double CalculateRadiusOnPhase() const { return CalculateRadiusOnPhase(m_Mass, m_Age, m_RZAMS0); } // Use class member variables - double CalculateRadiusOnPhase(const double p_Mass, const double p_Luminosity) const { std::cout << "MS::CalculateRadiusOnPhase(LUM) called\n"; return Radius(); } // Not a meaningful function for MS stars - double CalculateRadiusTransitionToHG(const double p_Mass, const double p_Age, double const p_RZAMS) const; + double CalculateRadiusOnPhase() const { return CalculateRadiusOnPhase(m_Mass, m_Tau, m_RZAMS0); } // Use class member variables + double CalculateRadiusOnPhase(const double p_Mass, const double p_Luminosity) const { return Radius(); } // Not a meaningful function for MS stars + double CalculateRadiusTransitionToHG(const double p_Mass, const double p_Tau, const double p_RZAMS) const; double CalculateTauAtPhaseEnd() const { return 1.0; } // tau = 1.0 at end of MS double CalculateTauOnPhase() const; diff --git a/src/Makefile b/src/Makefile index 5c24d7acc..631c17df6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,53 +1,24 @@ - -# use GNU C++ compiler by default -# -# can be overridden with CPP parameter -# -# e.g. make CPP=clang will use clang instead of g++ -# (note uppercase 'CPP' and no whitespace around '=') - CPP := g++ -# gsl directories -GSLINCDIR := /include -GSLLIBDIR := /lib - -# boost directories -BOOSTINCDIR := /include -BOOSTLIBDIR := /lib - -# hdf5 directories -HDF5INCDIR := /usr/include/hdf5/serial -HDF5LIBDIR := /usr/lib/x86_64-linux-gnu/hdf5/serial - -EXE := COMPAS - -# build COMPAS -ifeq ($(filter clean,$(MAKECMDGOALS)),) - $(info Building $(EXE) with $(CPP)) -endif - - -OPTFLAGS := -ifneq ($(filter fast,$(MAKECMDGOALS)),) - $(info Adding optimisation flags into the compilation - will take longer to build) - OPTFLAGS += -march=native -O3 -endif - -ifneq ($(filter staticfast,$(MAKECMDGOALS)),) - $(info Adding optimisation flags into the (static) compilation - will take longer to build) - OPTFLAGS += -march=native -O3 +# Build configuration: DOCKER_BUILD=1 for Docker, otherwise local +ifeq ($(DOCKER_BUILD),1) + # Docker-specific settings + ODIR := ../obj + BDIR := ../bin + OPTFLAGS_SPECIFIC := -O3 + CXXFLAGS_SPECIFIC := + LFLAGS_SPECIFIC := +else + # Local-specific settings + ODIR := obj + BDIR := bin + OPTFLAGS_SPECIFIC := -march=native -O3 + CXXFLAGS_SPECIFIC := -g -fnon-call-exceptions -Woverloaded-virtual + LFLAGS_SPECIFIC := -rdynamic endif - -CXXFLAGS := -std=c++17 -g -fnon-call-exceptions -Wall -Woverloaded-virtual $(OPTFLAGS) -ICFLAGS := -I$(GSLINCDIR) -I$(BOOSTINCDIR) -I$(HDF5INCDIR) -I. - -LIBS := -lm -lz -ldl -lpthread -GSLLIBS := -lgsl -lgslcblas -BOOSTLIBS := -lboost_filesystem -lboost_program_options -lboost_system -HDF5LIBS := -lhdf5_hl_cpp -lhdf5_cpp -lhdf5_hl -lhdf5 -LFLAGS := -L$(GSLLIBDIR) -L$(BOOSTLIBDIR) -L$(HDF5LIBDIR) -Xlinker -rpath -rdynamic -Xlinker $(BOOSTLIBDIR) $(HDF5LIBS) $(LIBS) $(GSLLIBS) $(BOOSTLIBS) +# Common variables for both builds +EXE := $(BDIR)/COMPAS SOURCES := \ profiling.cpp \ @@ -98,36 +69,68 @@ SOURCES := \ \ main.cpp -OBJI := $(SOURCES:.cpp=.o) +OBJS := $(patsubst %.cpp,$(ODIR)/%.o,$(SOURCES)) -# Create the list of header files, and remove -# main.h from this auto-generated list -INCL := $(SOURCES:.cpp=.h) -INCL := $(filter-out main.h,$(INCL)) +# GSL, Boost, and HDF5 directories +GSLINCDIR := /include +GSLLIBDIR := /lib -all: $(EXE) - @echo $(OBJI) +# boost directories +BOOSTINCDIR := /include +BOOSTLIBDIR := /lib -$(EXE): $(OBJI) - @echo $(SOURCES) - @echo $(OBJI) - $(CPP) $(OBJI) $(LFLAGS) -o $@ +# hdf5 directories +HDF5INCDIR := /usr/include/hdf5/serial +HDF5LIBDIR := /usr/lib/x86_64-linux-gnu/hdf5/serial + +# Define flags +OPTFLAGS := +ifneq ($(filter fast staticfast,$(MAKECMDGOALS)),) + $(info Adding optimisation flags into the compilation - will take longer to build) + OPTFLAGS += $(OPTFLAGS_SPECIFIC) +endif + +# might need -Wno-vla-cxx-extension for clang>18? +EXTRA_WARN_SUPPRESS := -Wno-vla-extension + +CXXFLAGS := -std=c++17 -Wall $(OPTFLAGS) $(CXXFLAGS_SPECIFIC) $(EXTRA_WARN_SUPPRESS) +ICFLAGS := -I$(GSLINCDIR) -I$(BOOSTINCDIR) -I$(HDF5INCDIR) -I. + +LIBS := -lm -lz -ldl -lpthread +GSLLIBS := -lgsl -lgslcblas +BOOSTLIBS := -lboost_filesystem -lboost_program_options -lboost_system +HDF5LIBS := -lhdf5_hl_cpp -lhdf5_cpp -lhdf5_hl -lhdf5 +LFLAGS := -L$(GSLLIBDIR) -L$(BOOSTLIBDIR) -L$(HDF5LIBDIR) -Xlinker -rpath -Xlinker $(BOOSTLIBDIR) $(HDF5LIBS) $(LIBS) $(GSLLIBS) $(BOOSTLIBS) $(LFLAGS_SPECIFIC) + +.PHONY: all static fast staticfast clean link + +# Main targets +all: $(EXE) link static: $(EXE)_STATIC - @echo $(OBJI) -$(EXE)_STATIC: $(OBJI) - @echo $(SOURCES) - @echo $(OBJI) - $(CPP) $(OBJI) $(LFLAGS) -static -o $@ +fast: all + +staticfast: static + +$(EXE): $(OBJS) + @mkdir -p $(BDIR) + $(CPP) $(OBJS) $(LFLAGS) -o $@ -%.o: %.cpp - $(CPP) $(CXXFLAGS) $(ICFLAGS) -c $? +$(EXE)_STATIC: $(OBJS) + @mkdir -p $(BDIR) + $(CPP) $(OBJS) $(LFLAGS) -static -o $@ -.phony: clean static fast staticfast +# Create a symbolic link for backward compatibility +link: $(EXE) + @ln -sf $(EXE) COMPAS -fast: $(EXE) -staticfast:$(EXE)_STATIC +# Pattern rule to compile source files into object files +$(ODIR)/%.o: %.cpp + @mkdir -p $(ODIR) + $(CPP) $(CXXFLAGS) $(ICFLAGS) -o $@ -c $< +# Clean-up rule clean: - rm -f $(OBJI) $(EXE) $(EXE)_STATIC + @echo "Removing generated files..." + @rm -rf $(ODIR) $(BDIR) COMPAS diff --git a/src/Makefile.docker b/src/Makefile.docker deleted file mode 100644 index 61917ee95..000000000 --- a/src/Makefile.docker +++ /dev/null @@ -1,137 +0,0 @@ - -# use GNU C++ compiler by default -# -# can be overridden with CPP parameter -# -# e.g. make CPP=clang will use clang instead of g++ -# (note uppercase 'CPP' and no whitespace around '=') - -CPP := g++ - -# gsl directories -GSLINCDIR := /include -GSLLIBDIR := /lib - -# boost directories -BOOSTINCDIR := /include -BOOSTLIBDIR := /lib - -# hdf5 directories -HDF5INCDIR := /usr/include/hdf5/serial -HDF5LIBDIR := /usr/lib/x86_64-linux-gnu/hdf5/serial - -ODIR := ../obj -BDIR := ../bin - -EXE := $(BDIR)/COMPAS - -# build COMPAS -ifeq ($(filter clean,$(MAKECMDGOALS)),) - $(info Building $(EXE) with $(CPP)) -endif - - -OPTFLAGS := -ifneq ($(filter fast,$(MAKECMDGOALS)),) - $(info Adding optimisation flags into the compilation - will take longer to build) - OPTFLAGS += -O3 -endif - -ifneq ($(filter staticfast,$(MAKECMDGOALS)),) - $(info Adding optimisation flags into the (static) compilation - will take longer to build) - OPTFLAGS += -O3 -endif - - -CXXFLAGS := -std=c++11 -Wall $(OPTFLAGS) -ICFLAGS := -I$(GSLINCDIR) -I$(BOOSTINCDIR) -I$(HDF5INCDIR) -I. - -LIBS := -lm -lz -ldl -lpthread -GSLLIBS := -lgsl -lgslcblas -BOOSTLIBS := -lboost_filesystem -lboost_program_options -lboost_system -HDF5LIBS := -lhdf5_hl_cpp -lhdf5_cpp -lhdf5_hl -lhdf5 -LFLAGS := -L$(GSLLIBDIR) -L$(BOOSTLIBDIR) -L$(HDF5LIBDIR) -Xlinker -rpath -Xlinker $(BOOSTLIBDIR) $(HDF5LIBS) $(LIBS) $(GSLLIBS) $(BOOSTLIBS) - -SOURCES := \ - profiling.cpp \ - utils.cpp \ - yaml.cpp \ - vector3d.cpp \ - \ - Rand.cpp \ - Options.cpp \ - Log.cpp \ - Errors.cpp \ - \ - BaseStar.cpp \ - \ - Star.cpp \ - \ - MainSequence.cpp \ - MS_lte_07.cpp \ - MS_gt_07.cpp \ - \ - CH.cpp \ - \ - GiantBranch.cpp \ - HG.cpp \ - FGB.cpp \ - CHeB.cpp \ - EAGB.cpp \ - TPAGB.cpp \ - \ - HeMS.cpp \ - HeHG.cpp \ - HeGB.cpp \ - \ - Remnants.cpp \ - \ - WhiteDwarfs.cpp \ - HeWD.cpp \ - COWD.cpp \ - ONeWD.cpp \ - \ - NS.cpp \ - BH.cpp \ - MR.cpp \ - \ - BinaryConstituentStar.cpp \ - BaseBinaryStar.cpp \ - BinaryStar.cpp \ - \ - main.cpp - -OBJI := $(SOURCES:.cpp=.o) -OBJS := $(patsubst %,$(ODIR)/%,$(OBJI)) - -# Create the list of header files, and remove -# main.h from this auto-generated list -INCL := $(SOURCES:.cpp=.h) -INCL := $(filter-out main.h,$(INCL)) - -all: $(EXE) - @echo $(OBJS) - -$(EXE): $(OBJS) - @echo $(SOURCES) - @echo $(OBJS) - $(CPP) $(OBJS) $(LFLAGS) -o $@ - -static: $(EXE)_STATIC - @echo $(OBJS) - -$(EXE)_STATIC: $(OBJS) - @echo $(SOURCES) - @echo $(OBJS) - $(CPP) $(OBJS) $(LFLAGS) -static -o $@ - -$(ODIR)/%.o: %.cpp - $(CPP) $(CXXFLAGS) $(ICFLAGS) -o $@ -c $? - -.phony: clean static fast staticfast - -fast: $(EXE) -staticfast:$(EXE)_STATIC - -clean: - rm -f $(OBJS) $(EXE) $(EXE)_STATIC diff --git a/src/NS.h b/src/NS.h index 0546ea2c1..a789371e6 100755 --- a/src/NS.h +++ b/src/NS.h @@ -101,6 +101,8 @@ class NS: virtual public BaseStar, public Remnants { double CalculateBirthMagneticField(); double CalculateBirthSpinPeriod(); + + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 0.0; } static double CalculateLuminosityOnPhase_Static(const double p_Mass, const double p_Time); double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase_Static(m_Mass, m_Age); } // Use class member variables diff --git a/src/ONeWD.h b/src/ONeWD.h index a4b757cc6..a0103b5d8 100755 --- a/src/ONeWD.h +++ b/src/ONeWD.h @@ -41,7 +41,6 @@ class ONeWD: virtual public BaseStar, public WhiteDwarfs { p_Time, p_Metallicity, WD_Baryon_Number.at(STELLAR_TYPE::OXYGEN_NEON_WHITE_DWARF)); } - protected: @@ -71,6 +70,7 @@ class ONeWD: virtual public BaseStar, public WhiteDwarfs { double CalculateLuminosityOnPhase() const { return CalculateLuminosityOnPhase(m_Mass, m_Age, m_Metallicity); } // Use class member variables + STELLAR_TYPE EvolveToNextPhase(); bool IsSupernova() const; bool ShouldEvolveOnPhase() const; diff --git a/src/Options.cpp b/src/Options.cpp index a88f12b53..ec62db46c 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -63,7 +63,7 @@ /* to the function AllowedOptionValues() here so that we can easily extract the */ /* allowed values for that option. */ /* */ -/* 10. Add the new option to one or more of the following vectors in Options.h, as */ +/* 10. Add the new option to the following vectors in Options.h, as */ /* required: */ /* */ /* m_ShorthandAllowed: options for which shorthand notation is allowed */ @@ -96,6 +96,30 @@ /* */ /******************************************************************************************/ +#include + +template +constexpr auto type_name() { + std::string_view name, prefix, suffix; +#ifdef __clang__ + name = __PRETTY_FUNCTION__; + prefix = "auto type_name() [T = "; + suffix = "]"; +#elif defined(__GNUC__) + name = __PRETTY_FUNCTION__; + prefix = "constexpr auto type_name() [with T = "; + suffix = "]"; +#elif defined(_MSC_VER) + name = __FUNCSIG__; + prefix = "auto __cdecl type_name<"; + suffix = ">(void)"; +#endif + name.remove_prefix(prefix.size()); + name.remove_suffix(suffix.size()); + return name; +} + + #include "Options.h" #include "changelog.h" @@ -105,16 +129,27 @@ namespace po = boost::program_options; namespace cls = po::command_line_style; namespace fs = boost::filesystem; -// this is required to set default value for boost program options of type vector -namespace std -{ - std::ostream& operator<<(std::ostream &os, const std::vector &vec) { - for (auto item : vec) os << item << " "; - return os; - } +namespace std { + // this is required to set default value for boost program options of type STR_VECTOR (std::vector) + std::ostream& operator<<(std::ostream &os, const STR_VECTOR &vec) { + for (auto item : vec) os << item << " "; + return os; + } + + // this is required to set default value for boost program options of type DBL_VECTOR (std::vector) + std::ostream& operator<<(std::ostream &os, const DBL_VECTOR &vec) { + for (auto item : vec) os << item << " "; + return os; + } } +#define OPT_VALUE1(optName, optValue, fallback) ((m_GridLine.optionValues.m_Populated && \ + (!m_GridLine.optionValues.m_VM[optName].defaulted() || !fallback)) \ + ? m_GridLine.optionValues.optValue \ + : m_CmdLine.optionValues.optValue) + + Options* Options::Instance() { if (!m_Instance) { m_Instance = new Options(); @@ -180,6 +215,9 @@ void Options::OptionValues::Initialise() { m_Quiet = false; m_RlofPrinting = true; + m_SystemSnapshotAgeThresholds.clear(); + m_SystemSnapshotTimeThresholds.clear(); + m_ShortHelp = true; m_StoreInputFiles = true; @@ -203,13 +241,16 @@ void Options::OptionValues::Initialise() { m_RandomSeed = 0; // Specify how long to evolve for - m_MaxEvolutionTime = 13700.0; + m_MaxEvolutionTime = HUBBLE_TIME / SECONDS_IN_MYR; //13700.0; m_MaxNumberOfTimestepIterations = 99999; m_TimestepsFileName = ""; m_MassChangeFraction = MAXIMUM_MASS_LOSS_FRACTION; m_RadialChangeFraction = MAXIMUM_RADIAL_CHANGE; + m_TimestepMultiplier = 1.0; + m_TimestepMultipliers.clear(); + // Initial mass options m_InitialMass = 5.0; @@ -230,7 +271,7 @@ void Options::OptionValues::Initialise() { m_MassRatioDistributionMin = 0.01; m_MassRatioDistributionMax = 1.0; - m_MinimumMassSecondary = 0.1; + m_MinimumSampledSecondaryMass = 0.1; // Initial orbit options @@ -275,12 +316,13 @@ void Options::OptionValues::Initialise() { // Kick options m_KickMagnitudeDistribution.type = KICK_MAGNITUDE_DISTRIBUTION::MULLERMANDEL; m_KickMagnitudeDistribution.typeString = KICK_MAGNITUDE_DISTRIBUTION_LABEL.at(m_KickMagnitudeDistribution.type); - m_KickMagnitudeDistributionSigmaCCSN_NS = 265; - m_KickMagnitudeDistributionSigmaCCSN_BH = 265; - m_KickMagnitudeDistributionMaximum = -1.0; + m_KickMagnitudeDistributionSigmaCCSN_NS = HOBBS_CORRECTED_SIGMA; // Best fit Maxwellian sigma for 48 younger than 10 Myr pulsars with proper motions from Disberg & Mandel (2025) sample; corrects Hobbs+ 2005 missing Jacobian + m_KickMagnitudeDistributionSigmaCCSN_BH = HOBBS_CORRECTED_SIGMA; + m_KickMagnitudeDistributionMaximum = -1.0; m_KickMagnitudeDistributionSigmaForECSN = 30.0; m_KickMagnitudeDistributionSigmaForUSSN = 30.0; m_KickScalingFactor = 1.0; + m_USSNKicksOverrideMandelMuller = false; // Default from Mandel&Mueller 2020 is to treat USSNe as normal CCSNe for kicks // Kick direction option m_KickDirectionDistribution.type = KICK_DIRECTION_DISTRIBUTION::ISOTROPIC; @@ -294,7 +336,8 @@ void Options::OptionValues::Initialise() { m_MullerMandelKickBH = MULLERMANDEL_KICKBH; m_MullerMandelKickNS = MULLERMANDEL_KICKNS; - m_MullerMandelSigmaKick = MULLERMANDEL_SIGMAKICK; + m_MullerMandelSigmaKickNS = MULLERMANDEL_SIGMAKICKNS; + m_MullerMandelSigmaKickBH = MULLERMANDEL_SIGMAKICKBH; // Kick magnitude random number (used to draw kick magnitude if necessary) m_KickMagnitudeRandom = 0.0; // actual value set later @@ -317,6 +360,10 @@ void Options::OptionValues::Initialise() { m_BlackHoleKicksMode.type = BLACK_HOLE_KICKS_MODE::FALLBACK; m_BlackHoleKicksMode.typeString = BLACK_HOLE_KICKS_MODE_LABEL.at(m_BlackHoleKicksMode.type); + m_MaltsevFallback = 0.5; + m_MaltsevMode.type = MALTSEV_MODE::BALANCED; + m_MaltsevMode.typeString = MALTSEV_MODE_LABEL.at(m_MaltsevMode.type); + // Rocket kicks m_RocketKickMagnitude1 = 0.0; m_RocketKickMagnitude2 = 0.0; @@ -328,8 +375,8 @@ void Options::OptionValues::Initialise() { // Chemically Homogeneous Evolution Mode m_CheMode.type = CHE_MODE::PESSIMISTIC; m_CheMode.typeString = CHE_MODE_LABEL.at(m_CheMode.type); - m_EnhanceCHELifetimesLuminosities = false; // default is don't enhance, as in Riley et al. - m_ScaleCHEMassLossWithSurfaceHeliumAbundance = false; // default is don't scale the mass loss, as in Riley et al. + m_EnhanceCHELifetimesLuminosities = true; // default is to enhance + m_ScaleCHEMassLossWithSurfaceHeliumAbundance = true; // default is to scale the CHE mass loss // Supernova remnant mass prescription options m_RemnantMassPrescription.type = REMNANT_MASS_PRESCRIPTION::MULLERMANDEL; @@ -376,14 +423,13 @@ void Options::OptionValues::Initialise() { // Mass loss options - m_UseMassLoss = true; m_CheckPhotonTiringLimit = false; m_EnableRotationallyEnhancedMassLoss = false; m_ExpelConvectiveEnvelopeAboveLuminosityThreshold = false; m_LuminosityToMassThreshold = 4.2; // Podsiadlowski, private communication - m_MassLossPrescription.type = MASS_LOSS_PRESCRIPTION::MERRITT2024; + m_MassLossPrescription.type = MASS_LOSS_PRESCRIPTION::MERRITT2025; m_MassLossPrescription.typeString = MASS_LOSS_PRESCRIPTION_LABEL.at(m_MassLossPrescription.type); m_LBVMassLossPrescription.type = LBV_MASS_LOSS_PRESCRIPTION::HURLEY_ADD; @@ -438,8 +484,8 @@ void Options::OptionValues::Initialise() { // Mass transfer angular momentum loss prescription options m_MassTransferJloss = 1.0; - m_MassTransferJlossMacLeodLinearFractionDegen = 0.5; - m_MassTransferJlossMacLeodLinearFractionNonDegen = 0.5; + m_MassTransferJlossLinearFractionDegen = 0.5; + m_MassTransferJlossLinearFractionNonDegen = 0.5; m_MassTransferAngularMomentumLossPrescription.type = MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::ISOTROPIC_RE_EMISSION; m_MassTransferAngularMomentumLossPrescription.typeString = MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION_LABEL.at(m_MassTransferAngularMomentumLossPrescription.type); @@ -485,10 +531,13 @@ void Options::OptionValues::Initialise() { m_CommonEnvelopeSlopeKruckow = -5.0 / 6.0; m_CommonEnvelopeAlphaThermal = 1.0; m_CommonEnvelopeLambdaMultiplier = 1.0; - m_CommonEnvelopeLambdaNanjingEnhanced = false; - m_CommonEnvelopeLambdaNanjingInterpolateInMass = false; - m_CommonEnvelopeLambdaNanjingInterpolateInMetallicity = false; + m_CommonEnvelopeLambdaNanjingEnhanced = true; + m_CommonEnvelopeLambdaNanjingInterpolateInMass = true; + m_CommonEnvelopeLambdaNanjingInterpolateInMetallicity = true; m_CommonEnvelopeLambdaNanjingUseRejuvenatedMass = false; + m_CommonEnvelopeSecondStageBeta = 0.0; + m_CommonEnvelopeSecondStageGammaPrescription.type = MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::ISOTROPIC_RE_EMISSION; + m_CommonEnvelopeSecondStageGammaPrescription.typeString = MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION_LABEL.at(m_CommonEnvelopeSecondStageGammaPrescription.type); m_AllowRadiativeEnvelopeStarToSurviveCommonEnvelope = false; m_AllowMainSequenceStarToSurviveCommonEnvelope = true; m_AllowImmediateRLOFpostCEToSurviveCommonEnvelope = false; @@ -608,17 +657,19 @@ void Options::OptionValues::Initialise() { m_LogfileCommonEnvelopes = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_COMMON_ENVELOPES)); m_LogfileCommonEnvelopesRecordTypes = -1; // all record types m_LogfileDetailedOutput = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_DETAILED_OUTPUT)); // assume BSE - get real answer when we know mode - m_LogfileDetailedOutputRecordTypes = -1; // all record types + m_LogfileDetailedOutputRecordTypes = 25; // record types 1, 4, & 5 (INITIAL_STATE, TIMESTEP_COMPLETED, and FINAL_STATE) m_LogfileDoubleCompactObjects = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_DOUBLE_COMPACT_OBJECTS)); m_LogfileDoubleCompactObjectsRecordTypes = -1; // all record types m_LogfilePulsarEvolution = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_PULSAR_EVOLUTION)); // only BSE for now - m_LogfilePulsarEvolutionRecordTypes = -1; // all record types + m_LogfilePulsarEvolutionRecordTypes = 4; // record type 3 ((pulsar) TIMESTEP_COMPLETED) m_LogfileRLOFParameters = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_RLOF_PARAMETERS)); m_LogfileRLOFParametersRecordTypes = -1; // all record types m_LogfileSupernovae = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SUPERNOVAE)); // assume BSE - get real answer when we know mode m_LogfileSupernovaeRecordTypes = -1; // all record types m_LogfileSwitchLog = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SWITCH_LOG)); // assume BSE - get real answer when we know mode - m_LogfileSystemParameters = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SYSTEM_PARAMETERS)); + m_LogfileSystemSnapshotLog = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG)); // assume BSE - get real answer when we know mode + m_LogfileSystemSnapshotLogRecordTypes = -1; // all record types + m_LogfileSystemParameters = std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SYSTEM_PARAMETERS)); // assume BSE - get real answer when we know mode m_LogfileSystemParametersRecordTypes = -1; // all record types m_AddOptionsToSysParms.type = ADD_OPTIONS_TO_SYSPARMS::GRID; @@ -658,40 +709,77 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt bool ok = true; // status - unless a problem occurs - // create default strings for std::vector types (too hard to do inline) + // create default strings for std::vector types (too hard to do inline) // debug classes std::string defaultDebugClasses; - std::ostringstream ss1; - for (auto debugClass = p_Options->m_DebugClasses.begin(); debugClass != p_Options->m_DebugClasses.end(); ++debugClass) ss1 << *debugClass << ","; - defaultDebugClasses = ss1.str(); + { + std::ostringstream ss; + for (auto debugClass = p_Options->m_DebugClasses.begin(); debugClass != p_Options->m_DebugClasses.end(); ++debugClass) ss << *debugClass << ","; + defaultDebugClasses = ss.str(); + } if (defaultDebugClasses.length() > 0) defaultDebugClasses.erase(defaultDebugClasses.length() - 1); defaultDebugClasses = "{" + defaultDebugClasses + "}"; // log classes std::string defaultLogClasses; - std::ostringstream ss2; - for (auto logClass = p_Options->m_LogClasses.begin(); logClass != p_Options->m_LogClasses.end(); ++logClass) ss2 << *logClass << ","; - defaultLogClasses = ss2.str(); + { + std::ostringstream ss; + for (auto logClass = p_Options->m_LogClasses.begin(); logClass != p_Options->m_LogClasses.end(); ++logClass) ss << *logClass << ","; + defaultLogClasses = ss.str(); + } if (defaultLogClasses.length() > 0) defaultLogClasses.erase(defaultLogClasses.length() - 1); defaultLogClasses = "{" + defaultLogClasses + "}"; // annotations std::string defaultNotes; - std::ostringstream ss3; - for (auto note = p_Options->m_Notes.begin(); note != p_Options->m_Notes.end(); ++note) ss3 << *note << ","; - defaultNotes = ss3.str(); + { + std::ostringstream ss; + for (auto note = p_Options->m_Notes.begin(); note != p_Options->m_Notes.end(); ++note) ss << *note << ","; + defaultNotes = ss.str(); + } if (defaultNotes.length() > 0) defaultNotes.erase(defaultNotes.length() - 1); defaultNotes = "{" + defaultNotes + "}"; // annotation headers std::string defaultNotesHdrs; - std::ostringstream ss4; - for (auto noteHdr = p_Options->m_NotesHdrs.begin(); noteHdr != p_Options->m_NotesHdrs.end(); ++noteHdr) ss4 << *noteHdr << ","; - defaultNotesHdrs = ss4.str(); + { + std::ostringstream ss; + for (auto noteHdr = p_Options->m_NotesHdrs.begin(); noteHdr != p_Options->m_NotesHdrs.end(); ++noteHdr) ss << *noteHdr << ","; + defaultNotesHdrs = ss.str(); + } if (defaultNotesHdrs.length() > 0) defaultNotesHdrs.erase(defaultNotesHdrs.length() - 1); defaultNotesHdrs = "{" + defaultNotesHdrs + "}"; + // phase-dependent timestep multipliers + std::string defaultTimestepMultipliers; + { + std::ostringstream ss; + for (auto multiplier = p_Options->m_TimestepMultipliers.begin(); multiplier != p_Options->m_TimestepMultipliers.end(); ++multiplier) ss << *multiplier << ","; + defaultTimestepMultipliers = ss.str(); + } + if (defaultTimestepMultipliers.length() > 0) defaultTimestepMultipliers.erase(defaultTimestepMultipliers.length() - 1); + defaultTimestepMultipliers = "{" + defaultTimestepMultipliers + "}"; + + // system detailed output age thresholds + std::string defaultSystemSnapshotAgeThresholds; + { + std::ostringstream ss; + for (auto threshold = p_Options->m_SystemSnapshotAgeThresholds.begin(); threshold != p_Options->m_SystemSnapshotAgeThresholds.end(); ++threshold) ss << *threshold << ","; + defaultSystemSnapshotAgeThresholds = ss.str(); + } + if (defaultSystemSnapshotAgeThresholds.length() > 0) defaultSystemSnapshotAgeThresholds.erase(defaultSystemSnapshotAgeThresholds.length() - 1); + defaultSystemSnapshotAgeThresholds = "{" + defaultSystemSnapshotAgeThresholds + "}"; + + // system detailed output time thresholds + std::string defaultSystemSnapshotTimeThresholds; + { + std::ostringstream ss; + for (auto threshold = p_Options->m_SystemSnapshotTimeThresholds.begin(); threshold != p_Options->m_SystemSnapshotTimeThresholds.end(); ++threshold) ss << *threshold << ","; + defaultSystemSnapshotTimeThresholds = ss.str(); + } + if (defaultSystemSnapshotTimeThresholds.length() > 0) defaultSystemSnapshotTimeThresholds.erase(defaultSystemSnapshotTimeThresholds.length() - 1); + defaultSystemSnapshotTimeThresholds = "{" + defaultSystemSnapshotTimeThresholds + "}"; // add options @@ -710,7 +798,7 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ( "help,h", - po::bool_switch(), "Print this help message" + po::bool_switch(), "Print this help message (-h is short form, --help includes more information)" ) ( "version,v", @@ -723,7 +811,7 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt // Note the "implicit_value()" (in the statements below) for boolean options is not the default option value // When specifying boolean options, users can specify just the option name (e.g. '--emit_gravitaional-radiation'), // or they can specify the option name, and a value for the option (e.g. '--emit_gravitaional-radiation true', or - // '--emit_gravitaional-radiation false') + // '--emit_gravitaional-radiation false') // The "implicit_value()" in the statements below specifies what value is assigned to the option if only the option name is // specified by the user (e.g. if the user specifies just '--emit_gravitaional-radiation', the implicit_value() for that option // will be assigned). @@ -931,20 +1019,20 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Print switch log to file (default = " + std::string(p_Options->m_SwitchLog ? "TRUE" : "FALSE") + ")").c_str() ) ( - "scale-CHE-mass-loss-with-surface-helium-abundance", - po::value(&p_Options->m_ScaleCHEMassLossWithSurfaceHeliumAbundance)->default_value(p_Options->m_ScaleCHEMassLossWithSurfaceHeliumAbundance)->implicit_value(true), - ("Whether to transition mass loss rates for chemically homogeneously evolving (CHE) stars between OB mass loss rates and Wolf-Rayet (WR) mass loss rates as a function of the surface helium abundance (Ys) as described by Yoon et al. 2006 (default = " + std::string(p_Options->m_ScaleCHEMassLossWithSurfaceHeliumAbundance ? "TRUE" : "FALSE") + ")").c_str() - ) - ( - "use-mass-loss", - po::value(&p_Options->m_UseMassLoss)->default_value(p_Options->m_UseMassLoss)->implicit_value(true), - ("Enable mass loss (default = " + std::string(p_Options->m_UseMassLoss ? "TRUE" : "FALSE") + ")").c_str() + "scale-CHE-mass-loss-with-surface-helium-abundance", + po::value(&p_Options->m_ScaleCHEMassLossWithSurfaceHeliumAbundance)->default_value(p_Options->m_ScaleCHEMassLossWithSurfaceHeliumAbundance)->implicit_value(true), + ("Whether to transition mass loss rates for chemically homogeneously evolving (CHE) between OB mass loss rates and Wolf-Rayet (WR) mass loss rates as a function of the surface helium abundance (Ys) as described by Yoon et al. 2006 (default = " + std::string(p_Options->m_ScaleCHEMassLossWithSurfaceHeliumAbundance ? "TRUE" : "FALSE") + ")").c_str() ) ( "use-mass-transfer", po::value(&p_Options->m_UseMassTransfer)->default_value(p_Options->m_UseMassTransfer)->implicit_value(true), ("Enable mass transfer (default = " + std::string(p_Options->m_UseMassTransfer ? "TRUE" : "FALSE") + ")").c_str() ) + ( + "USSN-kicks-override-mandel-muller", + po::value(&p_Options->m_USSNKicksOverrideMandelMuller)->default_value(p_Options->m_USSNKicksOverrideMandelMuller)->implicit_value(true), + ("Whether to use user-defined USSN kicks (as a fixed value) in lieu of the Mandel & Muller kick prescription for USSNe (default = " + std::string(p_Options->m_USSNKicksOverrideMandelMuller ? "TRUE" : "FALSE") + ")").c_str() + ) // numerical options - alphabetically grouped by type @@ -996,7 +1084,7 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ( "logfile-detailed-output-record-types", po::value(&p_Options->m_LogfileDetailedOutputRecordTypes)->default_value(p_Options->m_LogfileDetailedOutputRecordTypes), - ("Enabled record types for BSE Detailed Output logfile (default = " + std::to_string(p_Options->m_LogfileDetailedOutputRecordTypes) + ")").c_str() + ("Enabled record types for Detailed Output logfile (default = " + std::to_string(p_Options->m_LogfileDetailedOutputRecordTypes) + ")").c_str() ) ( "logfile-double-compact-objects-record-types", @@ -1018,6 +1106,11 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt po::value(&p_Options->m_LogfileSupernovaeRecordTypes)->default_value(p_Options->m_LogfileSupernovaeRecordTypes), ("Enabled record types for Supernovae logfile (default = " + std::to_string(p_Options->m_LogfileSupernovaeRecordTypes) + ")").c_str() ) + ( + "logfile-system-snapshot-log-record-types", + po::value(&p_Options->m_LogfileSystemSnapshotLogRecordTypes)->default_value(p_Options->m_LogfileSystemSnapshotLogRecordTypes), + ("Enabled record types for System Snapshot logfile (default = " + std::to_string(p_Options->m_LogfileSystemSnapshotLogRecordTypes) + ")").c_str() + ) ( "logfile-system-parameters-record-types", po::value(&p_Options->m_LogfileSystemParametersRecordTypes)->default_value(p_Options->m_LogfileSystemParametersRecordTypes), @@ -1089,6 +1182,11 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt po::value(&p_Options->m_CommonEnvelopeSlopeKruckow)->default_value(p_Options->m_CommonEnvelopeSlopeKruckow), ("Common Envelope slope for Kruckow lambda (default = " + std::to_string(p_Options->m_CommonEnvelopeSlopeKruckow) + ")").c_str() ) + ( + "common-envelope-second-stage-beta", + po::value(&p_Options->m_CommonEnvelopeSecondStageBeta)->default_value(p_Options->m_CommonEnvelopeSecondStageBeta), + ("Beta for second stage of 2-stage Common Envelope (default = " + std::to_string(p_Options->m_CommonEnvelopeSecondStageBeta) + ")").c_str() + ) ( "convective-envelope-mass-threshold", po::value(&p_Options->m_ConvectiveEnvelopeMassThreshold)->default_value(p_Options->m_ConvectiveEnvelopeMassThreshold), @@ -1238,19 +1336,19 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Initial mass (in Msol) for the secondary star (BSE) (default = " + std::to_string(p_Options->m_InitialMass2) + ")").c_str() ) ( - "initial-mass-max", + "initial-mass-function-max", po::value(&p_Options->m_InitialMassFunctionMax)->default_value(p_Options->m_InitialMassFunctionMax), - ("Maximum mass (in Msol) to generate using given IMF (default = " + std::to_string(p_Options->m_InitialMassFunctionMax) + ")").c_str() + ("The maximum mass (in Msol) to sample from the initial mass function (IMF), (only used when sampling initial mass, default = " + std::to_string(p_Options->m_InitialMassFunctionMax) + ")").c_str() ) ( - "initial-mass-min", + "initial-mass-function-min", po::value(&p_Options->m_InitialMassFunctionMin)->default_value(p_Options->m_InitialMassFunctionMin), - ("Minimum mass (in Msol) to generate using given IMF (default = " + std::to_string(p_Options->m_InitialMassFunctionMin) + ")").c_str() + ("The minimum mass (in Msol) to sample from the initial mass function (IMF), (only used when sampling initial mass, default = " + std::to_string(p_Options->m_InitialMassFunctionMin) + ")").c_str() ) ( - "initial-mass-power", + "initial-mass-function-power", po::value(&p_Options->m_InitialMassFunctionPower)->default_value(p_Options->m_InitialMassFunctionPower), - ("Single power law power to generate primary mass using POWERLAW IMF (default = " + std::to_string(p_Options->m_InitialMassFunctionPower) + ")").c_str() + ("The power to use when using the POWERLAW IMF (default = " + std::to_string(p_Options->m_InitialMassFunctionPower) + ")").c_str() ) ( @@ -1361,6 +1459,12 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Multiplicitive constant for LBV mass loss (default = " + std::to_string(p_Options->m_LuminousBlueVariableFactor) + ", use 10 for Mennekens & Vanbeveren 2014)").c_str() ) + ( + "maltsev-fallback", + po::value(&p_Options->m_MaltsevFallback)->default_value(p_Options->m_MaltsevFallback), + ("Fallback fraction for Maltsev black holes (ignored otherwise) (default = " + std::to_string(p_Options->m_MaltsevFallback) + ")").c_str() + ) + ( "mass-change-fraction", po::value(&p_Options->m_MassChangeFraction)->default_value(p_Options->m_MassChangeFraction), @@ -1393,14 +1497,14 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Fraction of specific angular momentum which non-accreted matter removes from the system (default = " + std::to_string(p_Options->m_MassTransferJloss) + ")").c_str() ) ( - "mass-transfer-jloss-macleod-linear-fraction-degen", - po::value(&p_Options->m_MassTransferJlossMacLeodLinearFractionDegen)->default_value(p_Options->m_MassTransferJlossMacLeodLinearFractionDegen), - ("Interpolation fraction for jloss prescription for degenerate accretors, requires --mass-transfer-angular-momentum-loss-prescription=MACLEOD_LINEAR. 0 is gamma_acc, 1 is gamma_L2 (default = " + std::to_string(p_Options->m_MassTransferJlossMacLeodLinearFractionDegen) + ")").c_str() + "mass-transfer-jloss-linear-fraction-degen", + po::value(&p_Options->m_MassTransferJlossLinearFractionDegen)->default_value(p_Options->m_MassTransferJlossLinearFractionDegen), + ("Interpolation fraction for jloss prescription for degenerate accretors, requires --mass-transfer-angular-momentum-loss-prescription=MACLEOD_LINEAR or KLENCKI_LINEAR. 0 is gamma_acc, 1 is gamma_L2 (default = " + std::to_string(p_Options->m_MassTransferJlossLinearFractionDegen) + ")").c_str() ) ( - "mass-transfer-jloss-macleod-linear-fraction-non-degen", - po::value(&p_Options->m_MassTransferJlossMacLeodLinearFractionNonDegen)->default_value(p_Options->m_MassTransferJlossMacLeodLinearFractionNonDegen), - ("Interpolation fraction for jloss prescription for non-degenerate accretors, requires --mass-transfer-angular-momentum-loss-prescription=MACLEOD_LINEAR. 0 is gamma_acc, 1 is gamma_L2 (default = " + std::to_string(p_Options->m_MassTransferJlossMacLeodLinearFractionNonDegen) + ")").c_str() + "mass-transfer-jloss-linear-fraction-non-degen", + po::value(&p_Options->m_MassTransferJlossLinearFractionNonDegen)->default_value(p_Options->m_MassTransferJlossLinearFractionNonDegen), + ("Interpolation fraction for jloss prescription for non-degenerate accretors, requires --mass-transfer-angular-momentum-loss-prescription=MACLEOD_LINEAR or KLENCKI_LINEAR. 0 is gamma_acc, 1 is gamma_L2 (default = " + std::to_string(p_Options->m_MassTransferJlossLinearFractionNonDegen) + ")").c_str() ) ( "mass-transfer-thermal-limit-C", @@ -1443,9 +1547,9 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Minimum metallicity to generate (default = " + std::to_string(p_Options->m_MetallicityDistributionMin) + ")").c_str() ) ( - "minimum-secondary-mass", - po::value(&p_Options->m_MinimumMassSecondary)->default_value(p_Options->m_MinimumMassSecondary), - ("Minimum mass of secondary to generate, in Msol (default = " + std::to_string(p_Options->m_MinimumMassSecondary) + ")").c_str() + "minimum-sampled-secondary-mass", + po::value(&p_Options->m_MinimumSampledSecondaryMass)->default_value(p_Options->m_MinimumSampledSecondaryMass), + ("Minimum sampled mass of secondary star, in Msol (default = " + std::to_string(p_Options->m_MinimumSampledSecondaryMass) + ")").c_str() ) ( "muller-mandel-kick-multiplier-BH", @@ -1458,9 +1562,14 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Scaling prefactor for NS kicks when using the 'MULLERMANDEL' kick magnitude distribution (default = " + std::to_string(p_Options->m_MullerMandelKickNS) + ")").c_str() ) ( - "muller-mandel-sigma-kick", - po::value(&p_Options->m_MullerMandelSigmaKick)->default_value(p_Options->m_MullerMandelSigmaKick), - ("Kick scatter when using the 'MULLERMANDEL' kick magnitude distribution (default = " + std::to_string(p_Options->m_MullerMandelSigmaKick) + ")").c_str() + "muller-mandel-sigma-kick-NS", + po::value(&p_Options->m_MullerMandelSigmaKickNS)->default_value(p_Options->m_MullerMandelSigmaKickNS), + ("NS kick scatter when using the 'MULLERMANDEL' kick magnitude distribution (default = " + std::to_string(p_Options->m_MullerMandelSigmaKickNS) + ")").c_str() + ) + ( + "muller-mandel-sigma-kick-BH", + po::value(&p_Options->m_MullerMandelSigmaKickBH)->default_value(p_Options->m_MullerMandelSigmaKickBH), + ("BH kick scatter when using the 'MULLERMANDEL' kick magnitude distribution (default = " + std::to_string(p_Options->m_MullerMandelSigmaKickBH) + ")").c_str() ) ( @@ -1643,12 +1752,27 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt po::value(&p_Options->m_SemiMajorAxisDistributionMin)->default_value(p_Options->m_SemiMajorAxisDistributionMin), ("Minimum semi-major axis, in AU, to generate (default = " + std::to_string(p_Options->m_SemiMajorAxisDistributionMin) + ")").c_str() ) + ( + "system-snapshot-age-thresholds", + po::value(&p_Options->m_SystemSnapshotAgeThresholds)->multitoken()->default_value(p_Options->m_SystemSnapshotAgeThresholds), + ("System detailed output logging system age thresholds (default = " + defaultSystemSnapshotAgeThresholds + ")").c_str() + ) + ( + "system-snapshot-time-thresholds", + po::value(&p_Options->m_SystemSnapshotTimeThresholds)->multitoken()->default_value(p_Options->m_SystemSnapshotTimeThresholds), + ("System detailed output logging simulation time thresholds (default = " + defaultSystemSnapshotTimeThresholds + ")").c_str() + ) ( "timestep-multiplier", po::value(&p_Options->m_TimestepMultiplier)->default_value(p_Options->m_TimestepMultiplier), ("Timestep multiplier for SSE and BSE on top of other choices, for use in debugging (default = " + std::to_string(p_Options->m_TimestepMultiplier) + ")").c_str() ) + ( + "timestep-multipliers", + po::value(&p_Options->m_TimestepMultipliers)->multitoken()->default_value(p_Options->m_TimestepMultipliers), + ("Phase-dependent timestep multipliers for SSE and BSE on top of other choices, for use in debugging (default = " + defaultTimestepMultipliers + ")").c_str() + ) ( "wolf-rayet-multiplier", @@ -1712,6 +1836,11 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt po::value(&p_Options->m_CommonEnvelopeMassAccretionPrescription.typeString)->default_value(p_Options->m_CommonEnvelopeMassAccretionPrescription.typeString), ("Assumption about whether NS/BHs can accrete mass during common envelope evolution (" + AllowedOptionValuesFormatted("common-envelope-mass-accretion-prescription") + ", default = '" + p_Options->m_CommonEnvelopeMassAccretionPrescription.typeString + "')").c_str() ) + ( + "common-envelope-second-stage-gamma-prescription", + po::value(&p_Options->m_CommonEnvelopeSecondStageGammaPrescription.typeString)->default_value(p_Options->m_CommonEnvelopeSecondStageGammaPrescription.typeString), + ("Second stage of 2-stage CE gamma prescription (" + AllowedOptionValuesFormatted("common-envelope-second-stage-gamma-prescription") + ", default = '" + p_Options->m_CommonEnvelopeSecondStageGammaPrescription.typeString + "')").c_str() + ) ( "create-YAML-file", po::value(&p_Options->m_YAMLfilename)->default_value(p_Options->m_YAMLfilename), @@ -1798,6 +1927,11 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt po::value(&p_Options->m_LogfileSupernovae)->default_value(p_Options->m_LogfileSupernovae), ("Filename for Supernovae logfile (default = " + p_Options->m_LogfileSupernovae + ")").c_str() ) + ( + "logfile-system-snapshot-log", + po::value(&p_Options->m_LogfileSystemSnapshotLog)->default_value(p_Options->m_LogfileSystemSnapshotLog), + ("Filename for System Snapshot logfile (default = " + p_Options->m_LogfileSystemSnapshotLog + ")").c_str() + ) ( "logfile-system-parameters", po::value(&p_Options->m_LogfileSystemParameters)->default_value(p_Options->m_LogfileSystemParameters), @@ -1834,6 +1968,11 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ("Main Sequence core mass prescription (" + AllowedOptionValuesFormatted("main-sequence-core-mass-prescription") + ", default = '" + p_Options->m_MainSequenceCoreMassPrescription.typeString + "')").c_str() ) + ( + "maltsev-mode", + po::value(&p_Options->m_MaltsevMode.typeString)->default_value(p_Options->m_MaltsevMode.typeString), + ("Maltsev mode (" + AllowedOptionValuesFormatted("maltsev-mode") + ", default = '" + p_Options->m_MaltsevMode.typeString + "')").c_str() + ) ( "mass-loss-prescription", po::value(&p_Options->m_MassLossPrescription.typeString)->default_value(p_Options->m_MassLossPrescription.typeString), @@ -1990,24 +2129,24 @@ bool Options::AddOptions(OptionValues *p_Options, po::options_description *p_Opt ( "debug-classes", - po::value>(&p_Options->m_DebugClasses)->multitoken()->default_value(p_Options->m_DebugClasses), + po::value(&p_Options->m_DebugClasses)->multitoken()->default_value(p_Options->m_DebugClasses), ("Debug classes enabled (default = " + defaultDebugClasses + ")").c_str() ) ( "log-classes", - po::value>(&p_Options->m_LogClasses)->multitoken()->default_value(p_Options->m_LogClasses), + po::value(&p_Options->m_LogClasses)->multitoken()->default_value(p_Options->m_LogClasses), ("Logging classes enabled (default = " + defaultLogClasses + ")").c_str() ) ( "notes", - po::value>(&p_Options->m_Notes)->multitoken()->default_value(p_Options->m_Notes), + po::value(&p_Options->m_Notes)->multitoken()->default_value(p_Options->m_Notes), ("User-specified annotations (default = " + defaultNotes + ")").c_str() ) ( "notes-hdrs", - po::value>(&p_Options->m_NotesHdrs)->multitoken()->default_value(p_Options->m_NotesHdrs), + po::value(&p_Options->m_NotesHdrs)->multitoken()->default_value(p_Options->m_NotesHdrs), ("User-specified annotation header strings (default = " + defaultNotesHdrs + ")").c_str() ) @@ -2102,7 +2241,7 @@ std::string Options::OptionValues::SetCalculatedOptionDefaults(const BOOST_MAP p if (phi1Defaulted || theta1Defaulted) { double phi1, theta1; - std::tie(phi1, theta1) = utils::DrawKickDirection(m_KickDirectionDistribution.type, m_KickDirectionPower); + std::tie(theta1, phi1) = utils::DrawKickDirection(m_KickDirectionDistribution.type, m_KickDirectionPower); if (phi1Defaulted) { m_KickPhi1 = phi1; if (p_UpdateMap == BOOST_MAP::UPDATE) { @@ -2125,7 +2264,7 @@ std::string Options::OptionValues::SetCalculatedOptionDefaults(const BOOST_MAP p if (phi2Defaulted || theta2Defaulted) { double phi2, theta2; - std::tie(phi2, theta2) = utils::DrawKickDirection(m_KickDirectionDistribution.type, m_KickDirectionPower); + std::tie(theta2, phi2) = utils::DrawKickDirection(m_KickDirectionDistribution.type, m_KickDirectionPower); if (phi2Defaulted) { m_KickPhi2 = phi2; if (p_UpdateMap == BOOST_MAP::UPDATE) { @@ -2231,6 +2370,11 @@ std::string Options::OptionValues::CheckAndSetOptions() { std::tie(found, m_CommonEnvelopeMassAccretionPrescription.type) = utils::GetMapKey(m_CommonEnvelopeMassAccretionPrescription.typeString, CE_ACCRETION_PRESCRIPTION_LABEL, m_CommonEnvelopeMassAccretionPrescription.type); COMPLAIN_IF(!found, "Unknown CE Mass Accretion Prescription"); } + + if (!DEFAULTED("common-envelope-second-stage-gamma-prescription")) { // second stagge of CE angular momentum loss prescription + std::tie(found, m_CommonEnvelopeSecondStageGammaPrescription.type) = utils::GetMapKey(m_CommonEnvelopeSecondStageGammaPrescription.typeString, MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION_LABEL, m_CommonEnvelopeSecondStageGammaPrescription.type); + COMPLAIN_IF(!found, "Unknown Mass Transfer Angular Momentum Loss Prescription for 2-stage CE"); + } if (!DEFAULTED("critical-mass-ratio-prescription")) { // critical mass ratio prescription std::tie(found, m_QCritPrescription.type) = utils::GetMapKey(m_QCritPrescription.typeString, QCRIT_PRESCRIPTION_LABEL, m_QCritPrescription.type); @@ -2287,6 +2431,11 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(!found, "Unknown Main Sequence Core Mass Prescription"); } + if (!DEFAULTED("maltsev-mode")) { // mass loss prescription + std::tie(found, m_MaltsevMode.type) = utils::GetMapKey(m_MaltsevMode.typeString, MALTSEV_MODE_LABEL, m_MaltsevMode.type); + COMPLAIN_IF(!found, "Unknown Mass Loss Prescription"); + } + if (!DEFAULTED("mass-loss-prescription")) { // mass loss prescription std::tie(found, m_MassLossPrescription.type) = utils::GetMapKey(m_MassLossPrescription.typeString, MASS_LOSS_PRESCRIPTION_LABEL, m_MassLossPrescription.type); COMPLAIN_IF(!found, "Unknown Mass Loss Prescription"); @@ -2299,7 +2448,7 @@ std::string Options::OptionValues::CheckAndSetOptions() { if (m_UseMassTransfer && !DEFAULTED("mass-transfer-accretion-efficiency-prescription")) { // mass transfer accretion efficiency prescription std::tie(found, m_MassTransferAccretionEfficiencyPrescription.type) = utils::GetMapKey(m_MassTransferAccretionEfficiencyPrescription.typeString, MT_ACCRETION_EFFICIENCY_PRESCRIPTION_LABEL, m_MassTransferAccretionEfficiencyPrescription.type); - COMPLAIN_IF(!found, "Unknown Mass Transfer Angular Momentum Loss Prescription"); + COMPLAIN_IF(!found, "Unknown Mass Transfer Efficiency Prescription"); } if (m_UseMassTransfer && !DEFAULTED("mass-transfer-angular-momentum-loss-prescription")) { // mass transfer angular momentum loss prescription @@ -2423,7 +2572,8 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(m_CommonEnvelopeMassAccretionConstant < 0.0, "CE mass accretion constant (--common-envelope-mass-accretion-constant) < 0"); COMPLAIN_IF(m_CommonEnvelopeMassAccretionMax < 0.0, "Maximum accreted mass (--common-envelope-mass-accretion-max) < 0"); COMPLAIN_IF(m_CommonEnvelopeMassAccretionMin < 0.0, "Minimum accreted mass (--common-envelope-mass-accretion-min) < 0"); - + COMPLAIN_IF(m_CommonEnvelopeSecondStageBeta < 0.0 || m_CommonEnvelopeSecondStageBeta > 0.0, "Mass transfer efficiency for second stage of 2-stage CE (--common-envelope-second-stage-beta) not in [0,1]"); + COMPLAIN_IF(m_CoolWindMassLossMultiplier < 0.0, "Wind mass loss multiplier for cool stars (--cool-wind-mass-loss-multiplier) < 0.0"); COMPLAIN_IF(m_DebugLevel < 0, "Debug level (--debug-level) < 0"); @@ -2441,13 +2591,13 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(m_HDF5BufferSize < 1, "HDF5 IO buffer size (--hdf5-buffer-size) must be >= 1"); COMPLAIN_IF(m_HDF5ChunkSize < HDF5_MINIMUM_CHUNK_SIZE, "HDF5 file dataset chunk size (--hdf5-chunk-size) must be >= minimum chunk size of " + std::to_string(HDF5_MINIMUM_CHUNK_SIZE)); - COMPLAIN_IF(m_InitialMass < MINIMUM_INITIAL_MASS || m_InitialMass > MAXIMUM_INITIAL_MASS, "Initial mass (--initial-mass) must be between " + std::to_string(MINIMUM_INITIAL_MASS) + " and " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); + COMPLAIN_IF(m_InitialMass < MINIMUM_INITIAL_MASS || m_InitialMass > MAXIMUM_INITIAL_MASS, "Initial mass (--initial-mass) must be between " + std::to_string(MINIMUM_INITIAL_MASS) + " and " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); COMPLAIN_IF(m_InitialMass1 < MINIMUM_INITIAL_MASS || m_InitialMass1 > MAXIMUM_INITIAL_MASS, "Primary initial mass (--initial-mass-1) must be between " + std::to_string(MINIMUM_INITIAL_MASS) + " and " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); COMPLAIN_IF(m_InitialMass2 < MINIMUM_INITIAL_MASS || m_InitialMass2 > MAXIMUM_INITIAL_MASS, "Secondary initial mass (--initial-mass-2) must be between " + std::to_string(MINIMUM_INITIAL_MASS) + " and " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); - COMPLAIN_IF(m_InitialMassFunctionMin < MINIMUM_INITIAL_MASS, "Minimum initial mass (--initial-mass-min) must be >= " + std::to_string(MINIMUM_INITIAL_MASS) + " Msol"); - COMPLAIN_IF(m_InitialMassFunctionMax > MAXIMUM_INITIAL_MASS, "Maximum initial mass (--initial-mass-max) must be <= " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); - COMPLAIN_IF(m_InitialMassFunctionMax <= m_InitialMassFunctionMin, "Maximum initial mass (--initial-mass-max) must be > Minimum initial mass (--initial-mass-min)"); + COMPLAIN_IF(m_InitialMassFunctionMin < MINIMUM_INITIAL_MASS, "Minimum mass to be sampled from the IMF (--initial-mass-function-min) must be >= " + std::to_string(MINIMUM_INITIAL_MASS) + " Msol"); + COMPLAIN_IF(m_InitialMassFunctionMax > MAXIMUM_INITIAL_MASS, "Maximum mass to be sampled from the IMF (--initial-mass-function-max) must be <= " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); + COMPLAIN_IF(m_InitialMassFunctionMax <= m_InitialMassFunctionMin, "Maximum mass to be sampled from the IMF (--initial-mass-function-max) must be > Minimum mass to be sampled from the IMF (--initial-mass-function-min)"); if (m_KickMagnitudeDistribution.type == KICK_MAGNITUDE_DISTRIBUTION::FLAT) { COMPLAIN_IF(m_KickMagnitudeDistributionMaximum <= 0.0, "User specified --kick-magnitude-distribution = FLAT with Maximum kick magnitude (--kick-magnitude-max) <= 0.0"); @@ -2456,9 +2606,11 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(m_LogLevel < 0, "Logging level (--log-level) < 0"); COMPLAIN_IF(m_LuminousBlueVariableFactor < 0.0, "LBV multiplier (--luminous-blue-variable-multiplier) < 0"); + + COMPLAIN_IF(m_MaltsevFallback < 0.0 || m_MaltsevFallback > 1.0, "Maltsev fallback fraction (--maltsev-fallback) must be between 0 and 1, inclusive"); COMPLAIN_IF(m_MassChangeFraction <= 0.0, "Mass change fraction per timestep (--mass-change-fraction) <= 0"); - + COMPLAIN_IF(m_MassRatio <= 0.0 || m_MassRatio > 1.0, "Mass ratio (--mass-ratio) must be greater than 0 and less than or equal to 1"); COMPLAIN_IF(m_MassRatioDistributionMin <= 0.0 || m_MassRatioDistributionMin > 1.0, "Minimum mass ratio (--mass-ratio-min) must be greater than 0 and less than or equal to 1"); @@ -2466,14 +2618,15 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(m_MassRatioDistributionMax <= m_MassRatioDistributionMin, "Maximum mass ratio (--mass-ratio-max) must be > Minimum mass ratio (--mass-ratio-min)"); COMPLAIN_IF(m_MaxEvolutionTime <= 0.0, "Maximum evolution time in Myr (--maxEvolutionTime) must be > 0"); + COMPLAIN_IF(m_MaxEvolutionTime > HUBBLE_TIME / SECONDS_IN_MYR, "Maximum evolution time in Myr (--maxEvolutionTime) must be <= " + std::to_string(HUBBLE_TIME / SECONDS_IN_MYR) + " Myr"); COMPLAIN_IF(m_Metallicity < MINIMUM_METALLICITY || m_Metallicity > MAXIMUM_METALLICITY, "Metallicity (--metallicity) should be absolute metallicity and must be between " + std::to_string(MINIMUM_METALLICITY) + " and " + std::to_string(MAXIMUM_METALLICITY)); COMPLAIN_IF(m_MetallicityDistributionMin < MINIMUM_METALLICITY || m_MetallicityDistributionMin > MAXIMUM_METALLICITY, "Minimum metallicity (--metallicity-min) must be between " + std::to_string(MINIMUM_METALLICITY) + " and " + std::to_string(MAXIMUM_METALLICITY)); COMPLAIN_IF(m_MetallicityDistributionMax < MINIMUM_METALLICITY || m_MetallicityDistributionMax > MAXIMUM_METALLICITY, "Maximum metallicity (--metallicity-max) must be between " + std::to_string(MINIMUM_METALLICITY) + " and " + std::to_string(MAXIMUM_METALLICITY)); COMPLAIN_IF(m_MetallicityDistributionMax <= m_MetallicityDistributionMin, "Maximum metallicity (--metallicity-max) must be > Minimum metallicity (--metallicity-min)"); - COMPLAIN_IF(m_MinimumMassSecondary < MINIMUM_INITIAL_MASS, "Secondary minimum mass (--minimum-secondary-mass) must be >= minimum initial mass of " + std::to_string(MINIMUM_INITIAL_MASS) + " Msol"); - COMPLAIN_IF(m_MinimumMassSecondary > MAXIMUM_INITIAL_MASS, "Secondary minimum mass (--minimum-secondary-mass) must be <= maximum initial mass of " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); + COMPLAIN_IF(m_MinimumSampledSecondaryMass < MINIMUM_INITIAL_MASS, "Minimum sampled secondary mass (--minimum-sampled-secondary-mass) must be >= " + std::to_string(MINIMUM_INITIAL_MASS) + " Msol"); + COMPLAIN_IF(m_MinimumSampledSecondaryMass > MAXIMUM_INITIAL_MASS, "Minimum sampled secondary mass (--minimum-sampled-secondary-mass) must be <= " + std::to_string(MAXIMUM_INITIAL_MASS) + " Msol"); if (m_NeutrinoMassLossAssumptionBH.type == NEUTRINO_MASS_LOSS_PRESCRIPTION::FIXED_MASS) { COMPLAIN_IF(m_NeutrinoMassLossValueBH < 0.0, "Neutrino mass loss value < 0"); @@ -2485,10 +2638,15 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(m_NeutrinoMassLossValueBH < 0.0 || m_NeutrinoMassLossValueBH > 1.0, "Neutrino mass loss must be between 0 and 1"); } - if (!DEFAULTED("notes")) { // user specified notes? - WARNUSER_IF(m_Notes.size() > Options::Instance()->NotesHdrs().size(), "WARNING: Annotations: more notes than headers - extra notes ignored"); // yes - check counts + // check for duplicate notes header strings + if (!DEFAULTED("notes-hdrs") && m_NotesHdrs.size() > 1) { + STR_VECTOR sortedHdrs(m_NotesHdrs.size()); + std::partial_sort_copy(begin(m_NotesHdrs), end(m_NotesHdrs), begin(sortedHdrs), end(sortedHdrs)); + COMPLAIN_IF(std::adjacent_find(std::begin(sortedHdrs), std::end(sortedHdrs)) != std::end(sortedHdrs), "Annotations: duplicate headers"); } + COMPLAIN_IF(!DEFAULTED("notes") && m_Notes.size() > Options::Instance()->NotesHdrs().size(), "Annotations: more notes than headers"); + COMPLAIN_IF(m_OrbitalPeriodDistributionMin < 0.0, "Minimum orbital period (--orbital-period-min) < 0"); COMPLAIN_IF(m_OrbitalPeriodDistributionMax < 0.0, "Maximum orbital period (--orbital-period-max) < 0"); COMPLAIN_IF(m_OrbitalPeriodDistributionMax <= m_OrbitalPeriodDistributionMin, "Maximum orbital period (--orbital-period-max) must be > Minimum orbital period (--orbital-period-min)"); @@ -2513,13 +2671,26 @@ std::string Options::OptionValues::CheckAndSetOptions() { COMPLAIN_IF(m_SemiMajorAxisDistributionMin < 0.0, "Minimum semi-major Axis (--semi-major-axis-min) < 0"); COMPLAIN_IF(m_SemiMajorAxisDistributionMax < 0.0, "Maximum semi-major Axis (--semi-major-axis-max) < 0"); + for (size_t idx = 0; idx < m_SystemSnapshotAgeThresholds.size(); idx++) { + COMPLAIN_IF(m_SystemSnapshotAgeThresholds[idx] < 0.0, "System snapshot age threshold (--system-snapshot-age-thresholds) index " + std::to_string(idx) + " < 0"); + COMPLAIN_IF(m_SystemSnapshotAgeThresholds[idx] > m_MaxEvolutionTime, "System snapshot age threshold (--system-snapshot-age-thresholds) index " + std::to_string(idx) + " > " + std::to_string(m_MaxEvolutionTime)); + } + + for (size_t idx = 0; idx < m_SystemSnapshotTimeThresholds.size(); idx++) { + COMPLAIN_IF(m_SystemSnapshotTimeThresholds[idx] < 0.0, "System detailed output time threshold (--system-snapshot-time-thresholds) index " + std::to_string(idx) + " < 0"); + COMPLAIN_IF(m_SystemSnapshotTimeThresholds[idx] > m_MaxEvolutionTime, "System snapshot age threshold (--system-snapshot-time-thresholds) index " + std::to_string(idx) + " > " + std::to_string(m_MaxEvolutionTime)); + } + COMPLAIN_IF(m_TimestepMultiplier <= 0.0, "Timestep multiplier (--timestep-multiplier) <= 0"); + COMPLAIN_IF(m_TimestepMultiplier > MAXIMUM_TIMESTEP_MULTIPLIER, "Timestep multiplier (--timestep-multiplier) > MAXIMUM (" + std::to_string(MAXIMUM_TIMESTEP_MULTIPLIER) + ")"); - COMPLAIN_IF(m_WolfRayetFactor < 0.0, "WR multiplier (--wolf-rayet-multiplier) < 0"); + COMPLAIN_IF(!DEFAULTED("timestep-multipliers") && m_TimestepMultipliers.size() > (static_cast(STELLAR_TYPE::COUNT) - 3), "More phase-dependent timestep multipliers than stellar types"); + for (size_t idx = 0; idx < m_TimestepMultipliers.size(); idx++) { + COMPLAIN_IF(m_TimestepMultipliers[idx] <= 0.0, "Phase-dependent timestep multiplier (--timestep-multipliers) <= 0 for stellar type index " + std::to_string(idx)); + COMPLAIN_IF(m_TimestepMultipliers[idx] > MAXIMUM_TIMESTEP_MULTIPLIER, "Phase-dependent timestep multiplier (--timestep-multipliers) > MAXIMUM (" + std::to_string(MAXIMUM_TIMESTEP_MULTIPLIER) + ") for stellar type index " + std::to_string(idx)); + } - COMPLAIN_IF(!DEFAULTED("initial-mass") && m_InitialMass <= 0.0, "Initial mass (--initial-mass) <= 0"); // initial mass must be > 0.0 - COMPLAIN_IF(!DEFAULTED("initial-mass-1") && m_InitialMass1 <= 0.0, "Primary initial mass (--initial-mass-1) <= 0"); // primary initial mass must be > 0.0 - COMPLAIN_IF(!DEFAULTED("initial-mass-2") && m_InitialMass2 <= 0.0, "Secondary initial mass (--initial-mass-2) <= 0"); // secondary initial mass must be > 0.0 + COMPLAIN_IF(m_WolfRayetFactor < 0.0, "WR multiplier (--wolf-rayet-multiplier) < 0"); COMPLAIN_IF(!DEFAULTED("semi-major-axis") && m_SemiMajorAxis <= 0.0, "Semi-major axis (--semi-major-axis) <= 0"); // semi-major axis must be > 0.0 COMPLAIN_IF(!DEFAULTED("orbital-period") && m_OrbitalPeriod <= 0.0, "Orbital period (--orbital-period) <= 0"); // orbital period must be > 0.0 @@ -2629,16 +2800,16 @@ void Options::BuildDefaultsMap(po::options_description *p_OptionsDescription) { * labels map. * * - * std::vector Options::AllowedOptionValues(const std::string p_OptionString) + * STR_VECTOR Options::AllowedOptionValues(const std::string p_OptionString) * * * @param [IN] p_OptionString String containing option name * @return Vector containing allowed option value strings */ -std::vector Options::AllowedOptionValues(const std::string p_OptionString) { +STR_VECTOR Options::AllowedOptionValues(const std::string p_OptionString) { #define POPULATE_RET(mapname) for (auto& it: mapname) ret.push_back("'" + it.second + "'") // for convenience and readability - undefined at end of function - std::vector ret; // initially empty + STR_VECTOR ret; // initially empty switch (_(p_OptionString.c_str())) { // which option? @@ -2649,6 +2820,7 @@ std::vector Options::AllowedOptionValues(const std::string p_Option case _("common-envelope-formalism") : POPULATE_RET(CE_FORMALISM_LABEL); break; case _("common-envelope-lambda-prescription") : POPULATE_RET(CE_LAMBDA_PRESCRIPTION_LABEL); break; case _("common-envelope-mass-accretion-prescription") : POPULATE_RET(CE_ACCRETION_PRESCRIPTION_LABEL); break; + case _("common-envelope-second-stage-gamma-prescription") : POPULATE_RET(MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION_LABEL); break; case _("critical-mass-ratio-prescription") : POPULATE_RET(QCRIT_PRESCRIPTION_LABEL); break; case _("envelope-state-prescription") : POPULATE_RET(ENVELOPE_STATE_PRESCRIPTION_LABEL); break; case _("eccentricity-distribution") : POPULATE_RET(ECCENTRICITY_DISTRIBUTION_LABEL); break; @@ -2660,6 +2832,7 @@ std::vector Options::AllowedOptionValues(const std::string p_Option case _("logfile-type") : POPULATE_RET(LOGFILETYPELabel); break; case _("LBV-mass-loss-prescription") : POPULATE_RET(LBV_MASS_LOSS_PRESCRIPTION_LABEL); break; case _("main-sequence-core-mass-prescription") : POPULATE_RET(CORE_MASS_PRESCRIPTION_LABEL); break; + case _("maltsev-mode") : POPULATE_RET(MALTSEV_MODE_LABEL); break; case _("mass-loss-prescription") : POPULATE_RET(MASS_LOSS_PRESCRIPTION_LABEL); break; case _("mass-ratio-distribution") : POPULATE_RET(MASS_RATIO_DISTRIBUTION_LABEL); break; case _("mass-transfer-accretion-efficiency-prescription") : POPULATE_RET(MT_ACCRETION_EFFICIENCY_PRESCRIPTION_LABEL); break; @@ -2716,7 +2889,7 @@ std::vector Options::AllowedOptionValues(const std::string p_Option */ std::string Options::AllowedOptionValuesFormatted(const std::string p_OptionString) { - std::vector allowedValues = AllowedOptionValues(p_OptionString); // get allowed values + STR_VECTOR allowedValues = AllowedOptionValues(p_OptionString); // get allowed values std::string str = "Options: ["; // formatted string for (size_t idx = 0; idx < allowedValues.size(); idx++) { // for each allowed value str += allowedValues[idx]; // show allowed value @@ -2899,20 +3072,22 @@ Options::ATTR Options::OptionAttributes(const po::variables_map p_VM, const po:: valueStr = p_VM[p_IT->first].as() ? "TRUE" : "FALSE"; } - else { // Assume std::vector - try { + else { // assume VECTOR + try { // try STR_VECTOR (std::vector) std::ostringstream elemsSS; elemsSS << "{ "; - std::vector tmp = p_VM[p_IT->first].as>(); - for (std::vector::iterator elem=tmp.begin(); elem != tmp.end(); elem++) { - elemsSS << "'" << (*elem) << "', "; + STR_VECTOR tmp = p_VM[p_IT->first].as(); + for (STR_VECTOR::iterator elem = tmp.begin(); elem != tmp.end(); elem++) { + std::string selem{*elem}; + if (selem.length() == 1 && selem == NOT_PROVIDED_STR) elemsSS << ", "; + else elemsSS << "'" << selem << "', "; } std::string elems = elemsSS.str(); if (elems.length() > 2) elems.erase(elems.length() - 2); else if (elems.length() == 2) elems.erase(elems.length() - 1); elems += " }"; - // the following options are declared as std::vector>: + // the following options are declared as STR_VECTOR: // // debug-classes // log-classes @@ -2928,10 +3103,38 @@ Options::ATTR Options::OptionAttributes(const po::variables_map p_VM, const po:: typeStr = "VECTOR"; valueStr = elems; } - catch (const boost::bad_any_cast &) { - dataType = TYPENAME::NONE; // unknown data type - typeStr = ""; - valueStr = ""; + catch (const boost::bad_any_cast &) { // not STR_VECTOR + try { // try DBL_VECTOR (std::vector) + std::ostringstream elemsSS; + elemsSS << "{ "; + DBL_VECTOR tmp = p_VM[p_IT->first].as(); + for (DBL_VECTOR::iterator elem = tmp.begin(); elem != tmp.end(); elem++) { + if (!isinf(*elem)) elemsSS << *elem; + elemsSS << ", "; + } + std::string elems = elemsSS.str(); + if (elems.length() > 2) elems.erase(elems.length() - 2); + else if (elems.length() == 2) elems.erase(elems.length() - 1); + elems += " }"; + + // the following options are declared as DBL_VECTOR: + // + // timestep-multipliers + // + // The vector of doubles is just formatted as a string here - with braces + // surrounding comma-separated values. + // + // We return dateType = TYPENAME::STRING, but typeStr = "VECTOR" + + dataType = TYPENAME::STRING; + typeStr = "VECTOR"; + valueStr = elems; + } + catch (const boost::bad_any_cast &) { // not DBL_VECTOR - unknown data type + dataType = TYPENAME::NONE; + typeStr = ""; + valueStr = ""; + } } } @@ -2967,7 +3170,7 @@ std::vector Options::OptionDetails(const OptionsDescriptorT &p_O std::string optionStr = it->first; // option string std::tie(dataType, defaulted, typeStr, valueStr) = OptionAttributes(p_Options.optionValues.m_VM, it); // get option attributes - std::vector allowedValues = AllowedOptionValues(optionStr); // get allowed option values if appropriate + STR_VECTOR allowedValues = AllowedOptionValues(optionStr); // get allowed option values if appropriate std::map::iterator mit; // iterator for defaults map std::string defaultStr = ""; // default string - initially empty @@ -3085,18 +3288,18 @@ bool Options::IsSupportedNumericDataType(TYPENAME p_TypeName) { * This function will expand this shorthand to the example shown above. There is no checking for correctness here - we just expand any * shorthand necessary and pass the argument vector back - correctness checking is done elsewhere. * - * The value for any omitted option values will be a string of length 1, with the char value NOT_PROVIDED (constant define in Options.h). - * Since at this stage the option names and values are just strings that will be parsed by boost, we don't need to worry about data type - - * code processing the options can check for NOT_PROVIDED and deal with it then. Since we may not know the maximum number of values - * expected (e.g. the number of notes-hdrs specifies the maximum number of notes expected, and we may not have that number yet), we leave - * it to later to pad out missing values beyond the last one specified here. e.g. a specification shuch as: + * The value for any omitted option values will be a string of length 1, with the char value NOT_PROVIDED_CHAR (constant defined in Options.h) + * as the first character of the string. Since at this stage the option names and values are just strings that will be parsed by boost, we + * don't need to worry about data type - code processing the options can check for NOT_PROVIDED_CHAR and deal with it then. Since we may not + * know the maximum number of values expected (e.g. the number of notes-hdrs specifies the maximum number of notes expected, and we may not + * have that number yet), we leave it to later to pad out missing values beyond the last one specified here. e.g. a specification shuch as: * * ./compas --notes-hdrs [hdrStr1,hdrStr2,hdrStr3,hdrStr4,hdrStr5] --notes ["note 1",,"this is note 3"] --option-name option-value ... * - * has notes 2, 4 & 5 omitted - we specify note 2 as NOT_PROVIDED here, and notes 4 & 5 will be added later. + * has notes 2, 4 & 5 omitted - we specify note 2 as NOT_PROVIDED_STR (string version of NOT_PROVIDED_CHAR) here, and notes 4 & 5 will be added later. * * - * std::tuple> ExpandShorthandOptionValues(int p_ArgCount, char *p_ArgStrings[]) + * std::tuple ExpandShorthandOptionValues(int p_ArgCount, char *p_ArgStrings[]) * * * @param [IN] p_ArgCount The number of argument strings. (note below for p_ArgStrings) @@ -3109,11 +3312,11 @@ bool Options::IsSupportedNumericDataType(TYPENAME p_TypeName) { * - an integer indicating the number of arguments (analogous to p_ArgCount) * - a vector of strings containing the arguments (analogous to p_ArgStrings) */ -std::tuple> Options::ExpandShorthandOptionValues(int p_ArgCount, char *p_ArgStrings[]) { +std::tuple Options::ExpandShorthandOptionValues(int p_ArgCount, char *p_ArgStrings[]) { std::string errStr = ""; // for now - std::vector strargs = {std::string(p_ArgStrings[0])}; // new args vector - command name is arg[0] + STR_VECTOR strargs = {std::string(p_ArgStrings[0])}; // new args vector - command name is arg[0] std::string argString = ""; // argument string std::string optionName = ""; // option name @@ -3159,7 +3362,7 @@ std::tuple> Options::ExpandShorthandO // (e.g. 'notes-hdrs' for 'notes'). So for now we just push a single default // value and deal with it later - strargs.push_back(NOT_PROVIDED); // "not provided" indicator" + strargs.push_back(NOT_PROVIDED_STR); // "not provided" indicator" } else { // no - defaults not allowed errStr = ERR_MSG(ERROR::MISSING_VALUE) + std::string(" for option '") + optionName + std::string("'"); // error @@ -3178,7 +3381,7 @@ std::tuple> Options::ExpandShorthandO } else { // empty value if (defaultAllowed) { // defaults allowed? - strargs.push_back(NOT_PROVIDED); // "not provided" indicator" + strargs.push_back(NOT_PROVIDED_STR); // "not provided" indicator" } else { // no - defaults not allowed errStr = ERR_MSG(ERROR::MISSING_VALUE) + std::string(" for option '") + optionName + std::string("'"); // error @@ -3189,7 +3392,7 @@ std::tuple> Options::ExpandShorthandO if (argString[argString.length() - 1] == ',') { // trailing comma in shorthand values? if (defaultAllowed) { // defaults allowed? - strargs.push_back(NOT_PROVIDED); // "not provided" indicator" + strargs.push_back(NOT_PROVIDED_STR); // "not provided" indicator" } else { // no - defaults not allowed errStr = ERR_MSG(ERROR::MISSING_VALUE) + std::string(" for option '") + optionName + std::string("'"); // error @@ -3248,7 +3451,7 @@ std::string Options::ParseOptionValues(int p_ArgCount, char *p_ArgStrings[], Opt std::string errStr = ""; // initially int argCount; // number or arg strings - std::vector sArgStrings; // arg strings - as std::strings + STR_VECTOR sArgStrings; // arg strings - as std::strings //********************************************************************// // first expand any shorthand notation used in the options // @@ -3276,9 +3479,9 @@ std::string Options::ParseOptionValues(int p_ArgCount, char *p_ArgStrings[], Opt p_OptionsDescriptor.optionsSpecified = {}; // initially p_OptionsDescriptor.complexOptionValues = {}; // initially - std::string optionName = ""; // option name - COMPLEX_TYPE type = COMPLEX_TYPE::NONE; // complex arg type (range, set, neither/none) - std::vector parms = {}; // the range or set parameters + std::string optionName = ""; // option name + COMPLEX_TYPE type = COMPLEX_TYPE::NONE; // complex arg type (range, set, neither/none) + STR_VECTOR parms = {}; // the range or set parameters for (size_t iArg = 1; iArg < (size_t)argCount; iArg++) { // for each arg string @@ -3401,7 +3604,7 @@ std::string Options::ParseOptionValues(int p_ArgCount, char *p_ArgStrings[], Opt // during binary/stellar evolution - helps to reduce the performance impact // of deprecated options and option values - std::vector fixedArgs {}; // vector args fixed for deprecated string + STR_VECTOR fixedArgs {}; // vector args fixed for deprecated string std::string thisArgString = ""; // arg string being checked std::string prevArgString = ""; // previous arg string (for option value checking) @@ -3446,15 +3649,60 @@ std::string Options::ParseOptionValues(int p_ArgCount, char *p_ArgStrings[], Opt // in the argument strings that will be passed to boost - so boost will fail // and complain about the offending parameter (which is what we want) + // before we pass the argstrings to parse_command_line() we need to remove the + // NOT_PROVIDED_CHAR for shorthand allowed, non-string, options (boost doesn't + // like it...) + + + bool shorthandOk = false; // flag to indicate if we're processing a shorthand allowed option + TYPENAME defaultType = TYPENAME::NONE; // type for default value + for (size_t iidx = 1; iidx < (size_t)argCount; iidx++) { // for each arg string + + std::string argStr = std::string(argStrings[iidx]); + if (!argStr.empty()) { // null arg? (shouldn't be...) + // no + if (argStr.substr(0, 2) == "--" || argStr[0] == '-' ) { // option string? + // yes + // strip the preamble + if (argStr.substr(0, 2) == "--") argStr = argStr.substr(2, argStr.length() - 2); + else argStr = argStr.substr(1, argStr.length() - 1); + + // check if option is on the shorthand allowed list + auto elem = std::find_if(m_ShorthandAllowed.begin(), m_ShorthandAllowed.end(), [&argStr](const SHORTHAND_ENTRY& e) { return std::get<0>(e) == argStr; }); + if (elem != m_ShorthandAllowed.end()) { // option in shorthand allowed list? + shorthandOk = elem != m_ShorthandAllowed.end(); // yes + defaultType = std::get<2>(*elem); // get type for option default + } + continue; // next arg string + } + + // processing arguments to options here - skipped over option name + if (shorthandOk) { // shorthand allowed? + // yes + switch (defaultType) { // which default value type? + case TYPENAME::STRING: break; // STRING - do nothing + case TYPENAME::DOUBLE: // DOUBLE - replace NOT_PROVIDED_CHAR with inf + if (*argStrings[iidx] == static_cast(NOT_PROVIDED_CHAR)) // NOT_PROVIDED_CHAR? + strcpy(argStrings[iidx], "inf"); // yes - replace with inf + break; + default: // shouldn't happen - for now we only have string and double default values + break; // do nothing - the parse will fail + } + + } + } + } + + // pass argstrings to boost parse_command_line() po::parsed_options const parsedOptions = po::parse_command_line(argCount, argStrings, p_OptionsDescriptor.optionDescriptions, cls::unix_style|cls::case_insensitive); // parse user-supplied options - po::store(parsedOptions, p_OptionsDescriptor.optionValues.m_VM); // store parsed options into variable map - po::notify(p_OptionsDescriptor.optionValues.m_VM); // populate the variables with option values + po::store(parsedOptions, p_OptionsDescriptor.optionValues.m_VM); // store parsed options into variable map + po::notify(p_OptionsDescriptor.optionValues.m_VM); // populate the variables with option values // this is our opportunity to distinguish between "-h" and "--help" (if specified) for (auto& entry : parsedOptions.options) { po::option_description const& opt = p_OptionsDescriptor.optionDescriptions.find(entry.string_key, false, false, false); - std::string originalTok = entry.original_tokens[0]; - std::string thisTok = utils::ToLower(utils::trim(originalTok)); + std::string originalTok = entry.original_tokens[0]; + std::string thisTok = utils::ToLower(utils::trim(originalTok)); if (!thisTok.empty()) { std::string shortOpt = utils::ToLower(opt.canonical_display_name(cls::allow_dash_for_short)); @@ -3848,7 +4096,7 @@ std::string Options::ParseOptionValues(int p_ArgCount, char *p_ArgStrings[], Opt // if we've made it this far we've parsed everything, and now is the time to fix up any default values // for vector options. Any vector option values that we know were not provided will have the value - // NOT_PROVIDED, so they can just be replaced with whatever the default value should be. However, + // NOT_PROVIDED_STR, so they can just be replaced with whatever the default value should be. However, // we couldn't really know until now how many values we should expect for vector options like 'notes', // because (in that case) the number of values expected is the number of 'notes-hdrs' specified by the // user, and that we didn't really know until now (and moreover, we didn't know what the command-line @@ -3861,21 +4109,21 @@ std::string Options::ParseOptionValues(int p_ArgCount, char *p_ArgStrings[], Opt for (auto& elem: m_ShorthandAllowed) { // for each option for which shorthand is allowed std::string optionName = std::get<0>(elem); // option name bool defaultAllowed = std::get<1>(elem); // omissions allowed? - std::string defaultString = std::get<2>(elem); // the COMPAS default for omissions if (defaultAllowed) { // omission allowed for this option? // yes switch (_(optionName.c_str())) { // which option? - // '--notes' is the only option affected at the moment + // '--notes' and '--timestep-multipliers' are the only options affected at the moment - case _("notes"): // notes + case _("notes"): { // notes + std::string defaultValue = std::string((std::get<3>(elem)).strVal); // the COMPAS default for omissions for (size_t idx = 0; idx < NotesHdrs().size(); idx++) { // for each specified notes-hdr if (idx < p_OptionsDescriptor.optionValues.m_Notes.size()) { // have parsed value? - if (p_OptionsDescriptor.optionValues.m_Notes[idx] == NOT_PROVIDED) { // yes - notes value provided? + if (p_OptionsDescriptor.optionValues.m_Notes[idx] == NOT_PROVIDED_STR) { // yes - notes value provided? // no - get default if (p_OptionsDescriptor.optionsOrigin == OPTIONS_ORIGIN::CMDLINE) { // from command line? - p_OptionsDescriptor.optionValues.m_Notes[idx] = defaultString; // yes - use COMPAS default + p_OptionsDescriptor.optionValues.m_Notes[idx] = defaultValue; // yes - use COMPAS default } else { // no - grid file line p_OptionsDescriptor.optionValues.m_Notes[idx] = m_CmdLine.optionValues.m_Notes[idx]; // use command-line value @@ -3884,15 +4132,39 @@ std::string Options::ParseOptionValues(int p_ArgCount, char *p_ArgStrings[], Opt } else { // no parsed value if (p_OptionsDescriptor.optionsOrigin == OPTIONS_ORIGIN::CMDLINE) { // from command line? - p_OptionsDescriptor.optionValues.m_Notes.push_back(defaultString); // yes - use COMPAS default + p_OptionsDescriptor.optionValues.m_Notes.push_back(defaultValue); // yes - use COMPAS default } else { // no - grid file line p_OptionsDescriptor.optionValues.m_Notes.push_back(m_CmdLine.optionValues.m_Notes[idx]); // use command-line value } } } - break; + } break; + case _("timestep-multipliers"): { // timestep-multipliers + double defaultValue = (std::get<3>(elem)).dblVal; // the COMPAS default for omissions + for (size_t idx = 0; idx < static_cast(STELLAR_TYPE::COUNT) - 3; idx++) { // for each stellar type (except STAR, BINARY_STAR, and NONE) + if (idx < p_OptionsDescriptor.optionValues.m_TimestepMultipliers.size()) { // have parsed value? + if (isinf(p_OptionsDescriptor.optionValues.m_TimestepMultipliers[idx])) { // yes - multiplier provided? + // no - get default + if (p_OptionsDescriptor.optionsOrigin == OPTIONS_ORIGIN::CMDLINE) { // from command line? + p_OptionsDescriptor.optionValues.m_TimestepMultipliers[idx] = defaultValue; // yes - use COMPAS default + } + else { // no - grid file line + p_OptionsDescriptor.optionValues.m_TimestepMultipliers[idx] = m_CmdLine.optionValues.m_TimestepMultipliers[idx]; // use command-line value + } + } + } + else { // no parsed value + if (p_OptionsDescriptor.optionsOrigin == OPTIONS_ORIGIN::CMDLINE) { // from command line? + p_OptionsDescriptor.optionValues.m_TimestepMultipliers.push_back(defaultValue); // yes - use COMPAS default + } + else { // no - grid file line + p_OptionsDescriptor.optionValues.m_TimestepMultipliers.push_back(m_CmdLine.optionValues.m_TimestepMultipliers[idx]); // use command-line value + } + } + } + } break; default: // default - shouldn't happen break; // do nothing - the parse will fail } @@ -3948,7 +4220,7 @@ bool Options::Initialise(int p_ArgCount, char *p_ArgStrings[]) { for (size_t idx = 0; idx < m_BSEOnly.size(); idx++) m_BSEOnly[idx] = utils::ToLower(utils::trim(m_BSEOnly[idx])); for (size_t idx = 0; idx < m_RangeExcluded.size(); idx++) m_RangeExcluded[idx] = utils::ToLower(utils::trim(m_RangeExcluded[idx])); for (size_t idx = 0; idx < m_SetExcluded.size(); idx++) m_SetExcluded[idx] = utils::ToLower(utils::trim(m_SetExcluded[idx])); - for (size_t idx = 0; idx < m_ShorthandAllowed.size(); idx--) m_ShorthandAllowed[idx] = std::make_tuple(utils::ToLower(utils::trim(std::get<0>(m_ShorthandAllowed[idx]))), std::get<1>(m_ShorthandAllowed[idx]), std::get<2>(m_ShorthandAllowed[idx])); + for (size_t idx = 0; idx < m_ShorthandAllowed.size(); idx--) m_ShorthandAllowed[idx] = std::make_tuple(utils::ToLower(utils::trim(std::get<0>(m_ShorthandAllowed[idx]))), std::get<1>(m_ShorthandAllowed[idx]), std::get<2>(m_ShorthandAllowed[idx]), std::get<3>(m_ShorthandAllowed[idx])); try { @@ -4270,10 +4542,10 @@ bool Options::InitialiseEvolvingObject(const std::string p_OptionsString) { // parse the option string (just as the OS/shell would do) - std::vector parsedStrings; // parsed option strings + STR_VECTOR parsedStrings; // parsed option strings size_t start = 0; // start position of parsed option string - size_t end = 0; // end position of parsed option strinf + size_t end = 0; // end position of parsed option string std::string delim = " "; // delimiter bool done = false; while (!done && end != std::string::npos) { // iterate over input string @@ -4668,6 +4940,8 @@ COMPAS_VARIABLE Options::OptionValue(const T_ANY_PROPERTY p_Property) const { case PROGRAM_OPTION::COMMON_ENVELOPE_MASS_ACCRETION_MIN : value = CommonEnvelopeMassAccretionMin(); break; case PROGRAM_OPTION::COMMON_ENVELOPE_MASS_ACCRETION_PRESCRIPTION : value = static_cast(CommonEnvelopeMassAccretionPrescription()); break; case PROGRAM_OPTION::COMMON_ENVELOPE_RECOMBINATION_ENERGY_DENSITY : value = CommonEnvelopeRecombinationEnergyDensity(); break; + case PROGRAM_OPTION::COMMON_ENVELOPE_SECOND_STAGE_BETA : value = CommonEnvelopeSecondStageBeta(); break; + case PROGRAM_OPTION::COMMON_ENVELOPE_SECOND_STAGE_GAMMA_PRESCRIPTION : value = static_cast(CommonEnvelopeSecondStageGammaPrescription()); break; case PROGRAM_OPTION::COMMON_ENVELOPE_SLOPE_KRUCKOW : value = CommonEnvelopeSlopeKruckow(); break; case PROGRAM_OPTION::CONVECTIVE_ENVELOPE_MASS_THRESHOLD : value = ConvectiveEnvelopeMassThreshold(); break; @@ -4699,7 +4973,7 @@ COMPAS_VARIABLE Options::OptionValue(const T_ANY_PROPERTY p_Property) const { case PROGRAM_OPTION::INITIAL_MASS_FUNCTION : value = static_cast(InitialMassFunction()); break; case PROGRAM_OPTION::INITIAL_MASS_FUNCTION_MAX : value = InitialMassFunctionMax(); break; case PROGRAM_OPTION::INITIAL_MASS_FUNCTION_MIN : value = InitialMassFunctionMin(); break; - case PROGRAM_OPTION::INITIAL_MASS_FUNCTIONPOWER : value = InitialMassFunctionPower(); break; + case PROGRAM_OPTION::INITIAL_MASS_FUNCTION_POWER : value = InitialMassFunctionPower(); break; case PROGRAM_OPTION::KICK_DIRECTION_DISTRIBUTION : value = static_cast(KickDirectionDistribution()); break; case PROGRAM_OPTION::KICK_DIRECTION_POWER : value = KickDirectionPower(); break; @@ -4729,7 +5003,10 @@ COMPAS_VARIABLE Options::OptionValue(const T_ANY_PROPERTY p_Property) const { case PROGRAM_OPTION::LBV_FACTOR : value = LuminousBlueVariableFactor(); break; case PROGRAM_OPTION::LBV_MASS_LOSS_PRESCRIPTION : value = static_cast(LBVMassLossPrescription()); break; - + + case PROGRAM_OPTION::MALTSEV_FALLBACK : value = MaltsevFallback(); break; + case PROGRAM_OPTION::MALTSEV_MODE : value = static_cast(MaltsevMode()); break; + case PROGRAM_OPTION::MASS_LOSS_PRESCRIPTION : value = static_cast(MassLossPrescription()); break; case PROGRAM_OPTION::MASS_RATIO : value = MassRatio(); break; @@ -4749,7 +5026,7 @@ COMPAS_VARIABLE Options::OptionValue(const T_ANY_PROPERTY p_Property) const { case PROGRAM_OPTION::METALLICITY_DISTRIBUTION_MAX : value = MetallicityDistributionMax(); break; case PROGRAM_OPTION::METALLICITY_DISTRIBUTION_MIN : value = MetallicityDistributionMin(); break; - case PROGRAM_OPTION::MINIMUM_MASS_SECONDARY : value = MinimumMassSecondary(); break; + case PROGRAM_OPTION::MINIMUM_SAMPLED_SECONDARY_MASS : value = MinimumSampledSecondaryMass(); break; case PROGRAM_OPTION::MT_ACCRETION_EFFICIENCY_PRESCRIPTION : value = static_cast(MassTransferAccretionEfficiencyPrescription()); break; case PROGRAM_OPTION::MT_ANG_MOM_LOSS_PRESCRIPTION : value = static_cast(MassTransferAngularMomentumLossPrescription()); break; @@ -4774,14 +5051,15 @@ COMPAS_VARIABLE Options::OptionValue(const T_ANY_PROPERTY p_Property) const { case PROGRAM_OPTION::MT_FRACTION_ACCRETED : value = MassTransferFractionAccreted(); break; case PROGRAM_OPTION::MT_JLOSS : value = MassTransferJloss(); break; - case PROGRAM_OPTION::MT_JLOSS_MACLEOD_LINEAR_FRACTION_DEGEN : value = MassTransferJlossMacLeodLinearFractionDegen(); break; - case PROGRAM_OPTION::MT_JLOSS_MACLEOD_LINEAR_FRACTION_NON_DEGEN : value = MassTransferJlossMacLeodLinearFractionNonDegen(); break; + case PROGRAM_OPTION::MT_JLOSS_LINEAR_FRACTION_DEGEN : value = MassTransferJlossLinearFractionDegen(); break; + case PROGRAM_OPTION::MT_JLOSS_LINEAR_FRACTION_NON_DEGEN : value = MassTransferJlossLinearFractionNonDegen(); break; case PROGRAM_OPTION::MT_REJUVENATION_PRESCRIPTION : value = static_cast(MassTransferRejuvenationPrescription()); break; case PROGRAM_OPTION::MT_THERMALLY_LIMITED_VARIATION : value = static_cast(MassTransferThermallyLimitedVariation()); break; case PROGRAM_OPTION::MULLER_MANDEL_KICK_MULTIPLIER_BH : value = MullerMandelKickMultiplierBH(); break; case PROGRAM_OPTION::MULLER_MANDEL_KICK_MULTIPLIER_NS : value = MullerMandelKickMultiplierNS(); break; - case PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK : value = MullerMandelSigmaKick(); break; + case PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK_BH : value = MullerMandelSigmaKickBH(); break; + case PROGRAM_OPTION::MULLER_MANDEL_SIGMA_KICK_NS : value = MullerMandelSigmaKickNS(); break; case PROGRAM_OPTION::NEUTRINO_MASS_LOSS_ASSUMPTION_BH : value = static_cast(NeutrinoMassLossAssumptionBH()); break; case PROGRAM_OPTION::NEUTRINO_MASS_LOSS_VALUE_BH : value = NeutrinoMassLossValueBH(); break; @@ -4852,6 +5130,7 @@ COMPAS_VARIABLE Options::OptionValue(const T_ANY_PROPERTY p_Property) const { case PROGRAM_OPTION::STELLAR_ZETA_PRESCRIPTION : value = static_cast(StellarZetaPrescription()); break; case PROGRAM_OPTION::TIDES_PRESCRIPTION : value = static_cast(TidesPrescription()); break; + case PROGRAM_OPTION::USSN_KICKS_OVERRIDE_MANDEL_MULLER : value = USSNKicksOverrideMandelMuller(); break; case PROGRAM_OPTION::WR_FACTOR : value = WolfRayetFactor(); break; diff --git a/src/Options.h b/src/Options.h index cba2a45cb..39a19fd01 100755 --- a/src/Options.h +++ b/src/Options.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "constants.h" @@ -34,8 +35,8 @@ namespace po = boost::program_options; - -const std::string NOT_PROVIDED = std::to_string(255); +const unsigned char NOT_PROVIDED_CHAR = 128; +const std::string NOT_PROVIDED_STR(1, static_cast(NOT_PROVIDED_CHAR)); // OPT_VALUE macro @@ -44,14 +45,14 @@ const std::string NOT_PROVIDED = std::to_string(255); // member variable is set to a value depending upon the value of the corresponding // option entered by the user. // -// Since users now specify grid line values using options, getter functions need to +// Since users specify grid line values using options, getter functions need to // know which option value to return - the one specified on the commandline (if in // fact the option was specified on the commandline), or the one specified on the // grid line (if in fact the option was specified on the grid line). // // The general idea is to use the value specified by the user on the grid line (if -// the use actually specified the option on the grid line) in preference to the -// value specified by the the use on the commandline (if the use actually specified +// the user actually specified the option on the grid line) in preference to the +// value specified by the user on the commandline (if the user actually specified // the option on the commandline). That's what the OPT_VALUE macro defined below // does - if the grid line exists (i.e. if a grid file is being used), the macro will // check whether the user specified the option on the grid line, and if they did return @@ -69,13 +70,19 @@ const std::string NOT_PROVIDED = std::to_string(255); // grid line // // 2. if 'fallback' is 'true': -// the value specified on the commandline IFF the user did not specify the -// option on the grid line but did specify the option on the commandline +// the value specified on the commandline if the user did not specify the +// option on the grid line (regardless of whether they specified the option +// on the commandline). In this case, if the user did not speify a value on +// the commandline, the commandline value is set according to the default +// behaviour for the option, and the grid line value is set from that. Note +// that for options whose default behaviours is to draw a random number, this +// will only be done once for the commandline value, and each grid line that +// falls back to the commandline will take the same value as the commandline. +// Consider using 'fallback' = 'false' for those cases. +// // else if 'fallback' is 'false': // the default value for the option // -// 3. the default value for the option -// // For most options we will use // // OPT_VALUE("option-name", m_OptionVar, true) @@ -185,6 +192,9 @@ class Options { // be "false" in the vector, and will be set true if and when the deprecation notice for that // option is shown the first time in a COMPAS run (a deprecation notice for a deprecated option // is only shown once per COMPAS run). + // - datestring indicating the date the option string was deprecated. Deprecated option strings + // should be manually removed from the "deprecatedOptionStrings" vector 12 months (too long?) + // after the deprecation date. Datestring format is yyyymmdd (e.g.20251107 indicates November 07, 2025). // // // "deprecatedOptionValues" vector @@ -209,22 +219,44 @@ class Options { // be "false" in the vector, and will be set true if and when the deprecation notice for that option // value is shown the first time in a COMPAS run (a deprecation notice for a deprecated option value // is only shown once per COMPAS run). - - std::vector> deprecatedOptionStrings = { - { "retain-core-mass-during-caseA-mass-transfer", "", false } + // - datestring indicating the date the option string was deprecated. Deprecated option values should + // be manually removed from the "deprecatedOptionValues" vector 12 months (too long?) after the + // deprecation date. Datestring format is yyyymmdd (e.g.20251107 indicates November 07, 2025). + + std::vector> deprecatedOptionStrings = { + { "retain-core-mass-during-caseA-mass-transfer", "", false, "20250116" }, + { "minimum-secondary-mass", "minimum-sampled-secondary-mass", false, "20250808" }, + { "initial-mass-max", "initial-mass-function-max", false, "20250808" }, + { "initial-mass-min", "initial-mass-function-min", false, "20250808" }, + { "initial-mass-power", "initial-mass-function-power", false, "20250808" }, + { "use-mass-loss", "mass-loss-prescription", false, "20250809" }, + { "mass-transfer-jloss-macleod-linear-fraction-degen", "mass-transfer-jloss-linear-fraction-degen", false, "20250819" }, + { "mass-transfer-jloss-macleod-linear-fraction-non-degen", "mass-transfer-jloss-linear-fraction-non-degen", false, "20250819" }, + { "scale-mass-loss-with-surface-helium-abundance", "scale-CHE-mass-loss-with-surface-helium-abundance", false, "20251209" }, }; - std::vector> deprecatedOptionValues = { - { "critical-mass-ratio-prescription", "GE20", "GE", false }, - { "critical-mass-ratio-prescription", "GE20_IC", "GE_IC", false }, - { "pulsational-pair-instability-prescription", "COMPAS", "WOOSLEY", false}, - { "pulsar-birth-spin-period-distribution", "ZERO", "NOSPIN", false } + std::vector> deprecatedOptionValues = { + { "critical-mass-ratio-prescription", "GE20", "GE", false, "20241118" }, + { "critical-mass-ratio-prescription", "GE20_IC", "GE_IC", false, "20241118" }, + { "pulsational-pair-instability-prescription", "COMPAS", "WOOSLEY", false, "20250208" }, + { "pulsar-birth-spin-period-distribution", "ZERO", "NOSPIN", false, "20250303" }, + { "tides-prescription", "KAPIL2024", "KAPIL2025", false, "20250525" }, + { "mass-loss-prescription", "MERRITT2024", "MERRITT2025", false, "20250717" }, + { "use-mass-loss", "TRUE", "MERRITT2025", true, "20250809" }, + { "use-mass-loss", "ON", "MERRITT2025", true, "20250809" }, + { "use-mass-loss", "YES", "MERRITT2025", true, "20250809" }, + { "use-mass-loss", "1", "MERRITT2025", true, "20250809" }, + { "use-mass-loss", "FALSE", "ZERO", true, "20250809" }, + { "use-mass-loss", "OFF", "ZERO", true, "20250809" }, + { "use-mass-loss", "NO", "ZERO", true, "20250809" }, + { "use-mass-loss", "0", "ZERO", true, "20250809" }, + { "main-sequence-core-mass-prescription", "ZERO", "HURLEY", false, "20251024" } }; // the following vector is used to replace deprecated options in the logfile-definitions file - std::vector> deprecatedOptionProperties = { - { "black_hole_kicks", "black_hole_kicks_mode", false }, - { "lbv_prescription", "LBV-mass-loss-prescription", false } + std::vector> deprecatedOptionProperties = { + { "black_hole_kicks", "black_hole_kicks_mode", false, "20241030" }, + { "lbv_prescription", "LBV_mass_loss_prescription", false, "20241030" } }; @@ -263,18 +295,34 @@ class Options { // complain (boost will only complain if the option/value pair is malformed or unknown, // which would almost certainly be the case - but it isn't guaranteed to be). - typedef std::tuple SHORTHAND_ENTRY; // option name, default allowed (i.e. can be omitted), default string + + union ShorthandDefault_t { + char* strVal = nullptr; + double dblVal; + ShorthandDefault_t() {} + ShorthandDefault_t(const std::string v) { strVal = new char[v.length() + 1]; strncpy(strVal, v.c_str(), v.length()); strVal[v.length()] = '\0'; } + ShorthandDefault_t(const double v) { dblVal = v; } + ~ShorthandDefault_t() {} + }; + + typedef std::tuple SHORTHAND_ENTRY; // option name, default allowed (i.e. can be omitted), default string + std::vector m_ShorthandAllowed = { // trying to keep entries alphabetical so easier to find specific entries - // option name default allowed default string - { "debug-classes", false, "" }, // don't allow defaults - we don't know how many classes to specify + // option name default allowed default value type default value + { "debug-classes", false, TYPENAME::STRING, ShorthandDefault_t(std::string()) }, // don't allow defaults - we don't know how many classes to specify + + { "log-classes", false, TYPENAME::STRING, ShorthandDefault_t(std::string()) }, // don't allow defaults - we don't know how many classes to specify - { "log-classes", false, "" }, // don't allow defaults - we don't know how many classes to specify + { "notes", true, TYPENAME::STRING, ShorthandDefault_t(std::string()) }, // allow defaults - number of notes is 0..#notes-hdrs + { "notes-hdrs", false, TYPENAME::STRING, ShorthandDefault_t(std::string()) }, // don't allow defaults - we don't know how many headers to specify - { "notes", true, "" }, // allow defaults - number of notes is 0..#notes-hdrs - { "notes-hdrs", false, "" } // don't allow defaults - we don't know how many headers to specify + { "timestep-multipliers", true, TYPENAME::DOUBLE, ShorthandDefault_t(1.0) }, // allow defaults - number of multipliers is the number of stellar types + + { "system-snapshot-age-thresholds", false, TYPENAME::DOUBLE, ShorthandDefault_t(-1.0) }, // don't allow defaults - no need + { "system-snapshot-time-thresholds", false, TYPENAME::DOUBLE, ShorthandDefault_t(-1.0) } // don't allow defaults - no need }; @@ -291,7 +339,7 @@ class Options { // the commandline and gridfile, but in the end I decided this way was actually // easier, cleaner, and gives us a bit more control. - std::vector m_GridLineExcluded = { + STR_VECTOR m_GridLineExcluded = { // trying to keep entries alphabetical so easier to find specific entries @@ -336,6 +384,8 @@ class Options { "logfile-supernovae", "logfile-supernovae-record-types", "logfile-switch-log", + "logfile-system-snapshot-log", + "logfile-system-snapshot-log-record-types", "logfile-system-parameters", "logfile-system-parameters-record-types", "logfile-type", @@ -358,8 +408,6 @@ class Options { "store-input-files", "switch-log", - "timestep-multiplier", - "version", "v", "yaml-template" @@ -398,7 +446,7 @@ class Options { // vectors is helping the user avoid duplicating stars/binaries if they specify // inconsistent options. - std::vector m_SSEOnly = { + STR_VECTOR m_SSEOnly = { // trying to keep enties alphabetical so easier to find specific entries @@ -410,7 +458,7 @@ class Options { "rotational-frequency" }; - std::vector m_BSEOnly = { + STR_VECTOR m_BSEOnly = { // trying to keep entries alphabetical so easier to find specific entries @@ -433,6 +481,8 @@ class Options { "common-envelope-mass-accretion-prescription", "common-envelope-recombination-energy-density", "common-envelope-slope-kruckow", + "common-envelope-second-stage-beta", + "common-envelope-second-stage-gamma-prescription", "eccentricity", "e", "eccentricity-distribution", @@ -468,6 +518,7 @@ class Options { "logfile-rlof-parameters", "logfile-rlof-parameters-record-types", + "maltsev-fallback", "mass-ratio", "q", "mass-ratio-max", "mass-ratio-min", @@ -475,15 +526,15 @@ class Options { "mass-transfer", "mass-transfer-fa", "mass-transfer-jloss", - "mass-transfer-jloss-macleod-linear-fraction-degen", - "mass-transfer-jloss-macleod-linear-fraction-non-degen", + "mass-transfer-jloss-linear-fraction-degen", + "mass-transfer-jloss-linear-fraction-non-degen", "mass-transfer-accretion-efficiency-prescription", "mass-transfer-angular-momentum-loss-prescription", "mass-transfer-rejuvenation-prescription", "mass-transfer-thermal-limit-accretor-multiplier", "mass-transfer-thermal-limit-C", "maximum-mass-donor-nandez-ivanova", - "minimum-secondary-mass", + "minimum-sampled-secondary-mass", "neutron-star-accretion-in-ce", @@ -503,7 +554,6 @@ class Options { "rocket-kick-theta-1", "rocket-kick-theta-2", - "scale-CHE-mass-loss-with-surface-helium-abundance", "semi-major-axis", "a", "semi-major-axis-distribution", "semi-major-axis-max", @@ -521,8 +571,7 @@ class Options { // but sets (and ranges) don't make sense for some options (things like "help", // "quiet", logfile names etc....) - - std::vector m_RangeExcluded = { + STR_VECTOR m_RangeExcluded = { // trying to keep entries alphabetical so easier to find specific entries @@ -599,11 +648,14 @@ class Options { "logfile-supernovae", "logfile-supernovae-record-types", "logfile-switch-log", + "logfile-system-snapshot-log", + "logfile-system-snapshot-log-record-types", "logfile-system-parameters", "logfile-system-parameters-record-types", "logfile-type", "main-sequence-core-mass-prescription", + "maltsev-mode", "mass-change-fraction", "mass-loss-prescription", "mass-ratio-distribution", @@ -648,13 +700,17 @@ class Options { "stellar-zeta-prescription", "store-input-files", "switch-log", + "system-snapshot-age-thresholds", + "system-snapshot-time-thresholds", "tides-prescription", "timesteps-filename", + "timestep-multipliers", "use-mass-loss", "use-mass-transfer", + "USSN-kicks-override-mandel-muller", "VMW-mass-loss-prescription", "version", "v", @@ -664,7 +720,7 @@ class Options { "yaml-template" }; - std::vector m_SetExcluded = { + STR_VECTOR m_SetExcluded = { // trying to keep entries alphabetical so easier to find specific entries @@ -709,6 +765,8 @@ class Options { "logfile-supernovae", "logfile-supernovae-record-types", "logfile-switch-log", + "logfile-system-snapshot-log", + "logfile-system-snapshot-log-record-types", "logfile-system-parameters", "logfile-system-parameters-record-types", "logfile-type", @@ -734,8 +792,11 @@ class Options { "store-input-files", "switch-log", + "system-snapshot-age-thresholds", + "system-snapshot-time-thresholds", "timesteps-filename", + "timestep-multipliers", "version", "v", @@ -778,8 +839,8 @@ class Options { bool m_EnableWarnings; // Flag used to determine if warnings (via SHOW_WARN macros) should be displayed ENUM_OPT m_FPErrorMode; // Specifies the mode for floating-point error handling - std::vector m_Notes; // Notes contents - for user-defined annotations - std::vector m_NotesHdrs; // Notes header strings - for user-defined annotations + STR_VECTOR m_Notes; // Notes contents - for user-defined annotations + STR_VECTOR m_NotesHdrs; // Notes header strings - for user-defined annotations bool m_EvolveDoubleWhiteDwarfs; // Whether to evolve double white dwarfs or not bool m_EvolveMainSequenceMergers; // Option to evolve binaries in which two stars merged on the main sequence @@ -799,6 +860,9 @@ class Options { bool m_Quiet; // Suppress some output bool m_RlofPrinting; // RLOF printing + DBL_VECTOR m_SystemSnapshotAgeThresholds; // System age thresholds for logging to system snapshot file + DBL_VECTOR m_SystemSnapshotTimeThresholds; // Simulation time thresholds for logging to system snapshot file + bool m_ShortHelp; // Flag to indicate whether user wants short help ('-h', just option names) or long help ('--help', plus descriptions) bool m_StoreInputFiles; // Store input files in output container (default = true) @@ -817,6 +881,7 @@ class Options { double m_MaxEvolutionTime; // Maximum time to evolve a binary by unsigned long int m_MaxNumberOfTimestepIterations; // Maximum number of timesteps to evolve binary for before giving up double m_TimestepMultiplier; // Multiplier for time step size (<1 -- shorter timesteps, >1 -- longer timesteps) + DBL_VECTOR m_TimestepMultipliers; // Phase-dependent multipliers for time step size (<1 -- shorter timesteps, >1 -- longer timesteps) double m_MassChangeFraction; // Approximate goal for fractional radial change per timestep double m_RadialChangeFraction; // Approximate goal for fractional radial change per timestep @@ -837,13 +902,17 @@ class Options { double m_InitialMassFunctionMax; // Maximum mass to generate in Msol double m_InitialMassFunctionPower; // single IMF power law set manually + // Maltsev remnant mass model + double m_MaltsevFallback; // fallback fraction for Maltsev fallback black holes + ENUM_OPT m_MaltsevMode; // Maltsev remnant mass mode (which variant of the prescription) + // Mass ratio double m_MassRatio; // Mass ratio for BSE ENUM_OPT m_MassRatioDistribution; // Which mass ratio distribution double m_MassRatioDistributionMin; // Minimum initial mass ratio when using a distribution double m_MassRatioDistributionMax; // Maximum initial mass ratio when using a distribution - double m_MinimumMassSecondary; // Minimum mass of secondary to draw (in Msol) + double m_MinimumSampledSecondaryMass; // Minimum mass of secondary to draw when sampling (in Msol) // Semi major axis double m_SemiMajorAxis; // Semi-major axis @@ -871,11 +940,11 @@ class Options { // Kick options ENUM_OPT m_KickMagnitudeDistribution; // Which kick magnitude distribution - double m_KickMagnitudeDistributionSigmaCCSN_NS; // Kick magnitude sigma in km s^-1 for neutron stars (default = "250" ) - double m_KickMagnitudeDistributionSigmaCCSN_BH; // Kick magnitude sigma in km s^-1 for black holes (default = "250" ) + double m_KickMagnitudeDistributionSigmaCCSN_NS; // Kick magnitude sigma in km s^-1 for neutron stars + double m_KickMagnitudeDistributionSigmaCCSN_BH; // Kick magnitude sigma in km s^-1 for black holes double m_KickMagnitudeDistributionMaximum; // Maximum kick magnitude to draw. If negative, no maximum - double m_KickMagnitudeDistributionSigmaForECSN; // Kick magnitude sigma for ECSN in km s^-1 (default = "0" ) - double m_KickMagnitudeDistributionSigmaForUSSN; // Kick magnitude sigma for USSN in km s^-1 (default = "20" ) + double m_KickMagnitudeDistributionSigmaForECSN; // Kick magnitude sigma for ECSN in km s^-1 + double m_KickMagnitudeDistributionSigmaForUSSN; // Kick magnitude sigma for USSN in km s^-1 double m_KickScalingFactor; // Arbitrary factor for scaling kicks // Kick direction options @@ -898,7 +967,10 @@ class Options { double m_MullerMandelKickBH; // Multiplier for BH kicks per Mandel and Mueller, 2020 double m_MullerMandelKickNS; // Multiplier for NS kicks per Mandel and Mueller, 2020 - double m_MullerMandelSigmaKick; // Scatter for kicks per Mandel and Mueller, 2020 + double m_MullerMandelSigmaKickBH; // Scatter for BH kicks per Mandel and Mueller, 2020 + double m_MullerMandelSigmaKickNS; // Scatter for NS kicks per Mandel and Mueller, 2020 + bool m_USSNKicksOverrideMandelMuller; // Use user-defined USSN kicks (as a fixed value) in lieu of the Mandel & Muller kick prescription for USSNe + // Black hole kicks ENUM_OPT m_BlackHoleKicksMode; // Which black hole kicks mode @@ -952,7 +1024,6 @@ class Options { std::string m_OutputContainerName; // Name of output container (directory) // Mass loss options - bool m_UseMassLoss; // Whether to activate mass loss (default = True) bool m_CheckPhotonTiringLimit; // Whether to check the photon tiring limit for wind mass loss // Can also have options for modifying strength of winds etc here @@ -997,8 +1068,8 @@ class Options { ENUM_OPT m_MassTransferThermallyLimitedVariation; // Choose how to deal with mass transfer if it is set as thermally limited. double m_MassTransferJloss; // Specific angular momentum of the material leaving the system (not accreted) - double m_MassTransferJlossMacLeodLinearFractionDegen; // Linear interpolation fraction for jloss for degenerate accretors, between accretor and L2 position - double m_MassTransferJlossMacLeodLinearFractionNonDegen; // Linear interpolation fraction for jloss for non-degenerate accretors, between accretor and L2 position + double m_MassTransferJlossLinearFractionDegen; // Linear interpolation fraction for jloss for degenerate accretors, between accretor and L2 position (either Macleod or Klencki linear) + double m_MassTransferJlossLinearFractionNonDegen; // Linear interpolation fraction for jloss for non-degenerate accretors, between accretor and L2 position (either Macleod or Klencki linear) ENUM_OPT m_MassTransferAngularMomentumLossPrescription; // Which mass transfer angular momentum loss prescription // Mass transfer rejuvenation prescription @@ -1031,8 +1102,10 @@ class Options { double m_MassTransferCriticalMassRatioWhiteDwarfDegenerateAccretor; // Critical mass ratio for MT from a white dwarf on to a degenerate accretor // Common Envelope options - double m_CommonEnvelopeAlpha; // Common envelope efficiency alpha parameter (default = X) - double m_CommonEnvelopeLambda; // Common envelope Lambda parameter (default = X) + double m_CommonEnvelopeAlpha; // Common envelope efficiency alpha parameter + double m_CommonEnvelopeLambda; // Common envelope Lambda parameter + double m_CommonEnvelopeSecondStageBeta; // Mass transfer efficiency for second stage of 2-stage common envelope + ENUM_OPT m_CommonEnvelopeSecondStageGammaPrescription; // Angular momentum loss prescription for second stage of 2-stage common envelope double m_CommonEnvelopeSlopeKruckow; // Common envelope power factor for Kruckow fit normalized according to Kruckow+2016, Fig. 1 double m_CommonEnvelopeAlphaThermal; // lambda = alpha_th*lambda_b + (1-alpha_th)*lambda_g double m_CommonEnvelopeLambdaMultiplier; // Multiply common envelope lambda by some constant @@ -1126,10 +1199,10 @@ class Options { // Debug and logging options int m_DebugLevel; // Debug level - used to determine which debug statements are actually written - std::vector m_DebugClasses; // Debug classes - used to determine which debug statements are actually written + STR_VECTOR m_DebugClasses; // Debug classes - used to determine which debug statements are actually written int m_LogLevel; // Logging level - used to determine which logging statements are actually written - std::vector m_LogClasses; // Logging classes - used to determine which logging statements are actually written + STR_VECTOR m_LogClasses; // Logging classes - used to determine which logging statements are actually written // Logfiles @@ -1138,6 +1211,7 @@ class Options { ENUM_OPT m_LogfileType; // File type log files std::string m_LogfileSystemParameters; // output file name: system parameters + std::string m_LogfileSystemSnapshotLog; // output file name: system snapshot std::string m_LogfileDetailedOutput; // output file name: detailed output std::string m_LogfileDoubleCompactObjects; // output file name: double compact objects std::string m_LogfileSupernovae; // output file name: supernovae @@ -1147,6 +1221,7 @@ class Options { std::string m_LogfileSwitchLog; // output file name: switch log int m_LogfileSystemParametersRecordTypes; // enabled record types: system parameters + int m_LogfileSystemSnapshotLogRecordTypes; // enabled record types: system snapshot int m_LogfileDetailedOutputRecordTypes; // enabled record types: detailed output int m_LogfileDoubleCompactObjectsRecordTypes; // enabled record types: double compact objects int m_LogfileSupernovaeRecordTypes; // enabled record types: supernovae @@ -1204,7 +1279,7 @@ class Options { // // type (INT) type indicates whether the entry refers to a RANGE (type 0) or SET (type 1) // dataType (TYPENAME) the data type of the option to which the RangeOrSetDescriptorT pertaines - // parameters (std::vector) a vector of strings that hold the parameters as they were supplied by the user + // parameters (STR_VECTOR) a vector of strings that hold the parameters as they were supplied by the user // for a RANGE there must be exactly 3 parameters: start, count, increment // a SET must have at least one parameter (element); there is no maximum number of elements // rangeParms (std::vector) numerical values for range parameters (see RangeParameter struct) @@ -1225,7 +1300,7 @@ class Options { typedef struct RangeOrSetDescriptor { COMPLEX_TYPE type; // RANGE or SET TYPENAME dataType; // the option datatype - std::vector parameters; // the range or set parameters + STR_VECTOR parameters; // the range or set parameters std::vector rangeParms; // range parameters numerical values int currPos; // current position of iterator - count for RANGE, pos for SET } RangeOrSetDescriptorT; @@ -1281,13 +1356,13 @@ class Options { // member functions bool AddOptions(OptionValues *p_Options, po::options_description *p_OptionsDescription); - std::vector AllowedOptionValues(const std::string p_OptionString); + STR_VECTOR AllowedOptionValues(const std::string p_OptionString); std::string AllowedOptionValuesFormatted(const std::string p_OptionString); int AdvanceOptionVariation(OptionsDescriptorT &p_OptionsDescriptor); void BuildDefaultsMap(po::options_description *p_OptionsDescription); - std::tuple> ExpandShorthandOptionValues(int p_ArgCount, char *p_ArgStrings[]); + std::tuple ExpandShorthandOptionValues(int p_ArgCount, char *p_ArgStrings[]); bool IsSupportedNumericDataType(TYPENAME p_TypeName); @@ -1371,6 +1446,8 @@ class Options { double CommonEnvelopeMassAccretionMin() const { return OPT_VALUE("common-envelope-mass-accretion-min", m_CommonEnvelopeMassAccretionMin, true); } CE_ACCRETION_PRESCRIPTION CommonEnvelopeMassAccretionPrescription() const { return OPT_VALUE("common-envelope-mass-accretion-prescription", m_CommonEnvelopeMassAccretionPrescription.type, true); } double CommonEnvelopeRecombinationEnergyDensity() const { return OPT_VALUE("common-envelope-recombination-energy-density", m_CommonEnvelopeRecombinationEnergyDensity, true); } + double CommonEnvelopeSecondStageBeta() const { return OPT_VALUE("common-envelope-second-stage-beta", m_CommonEnvelopeSecondStageBeta, true); } + MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION CommonEnvelopeSecondStageGammaPrescription() const { return OPT_VALUE("common-envelope-second-stage-gamma-prescription", m_CommonEnvelopeSecondStageGammaPrescription.type, true); } double CommonEnvelopeSlopeKruckow() const { return OPT_VALUE("common-envelope-slope-kruckow", m_CommonEnvelopeSlopeKruckow, true); } double ConvectiveEnvelopeMassThreshold() const { return OPT_VALUE("convective-envelope-mass-threshold", m_ConvectiveEnvelopeMassThreshold, true); } @@ -1378,7 +1455,7 @@ class Options { double CoolWindMassLossMultiplier() const { return OPT_VALUE("cool-wind-mass-loss-multiplier", m_CoolWindMassLossMultiplier, true); } - std::vector DebugClasses() const { return m_CmdLine.optionValues.m_DebugClasses; } + STR_VECTOR DebugClasses() const { return m_CmdLine.optionValues.m_DebugClasses; } int DebugLevel() const { return m_CmdLine.optionValues.m_DebugLevel; } bool DebugToFile() const { return m_CmdLine.optionValues.m_DebugToFile; } bool DetailedOutput() const { return m_CmdLine.optionValues.m_DetailedOutput; } @@ -1424,9 +1501,9 @@ class Options { double InitialMass2() const { return OPT_VALUE("initial-mass-2", m_InitialMass2, true); } INITIAL_MASS_FUNCTION InitialMassFunction() const { return OPT_VALUE("initial-mass-function", m_InitialMassFunction.type, true); } - double InitialMassFunctionMax() const { return OPT_VALUE("initial-mass-max", m_InitialMassFunctionMax, true); } - double InitialMassFunctionMin() const { return OPT_VALUE("initial-mass-min", m_InitialMassFunctionMin, true); } - double InitialMassFunctionPower() const { return OPT_VALUE("initial-mass-power", m_InitialMassFunctionPower, true); } + double InitialMassFunctionMax() const { return OPT_VALUE("initial-mass-function-max", m_InitialMassFunctionMax, true); } + double InitialMassFunctionMin() const { return OPT_VALUE("initial-mass-function-min", m_InitialMassFunctionMin, true); } + double InitialMassFunctionPower() const { return OPT_VALUE("initial-mass-function-power", m_InitialMassFunctionPower, true); } KICK_DIRECTION_DISTRIBUTION KickDirectionDistribution() const { return OPT_VALUE("kick-direction-distribution", m_KickDirectionDistribution.type, true); } double KickDirectionPower() const { return OPT_VALUE("kick-direction-power", m_KickDirectionPower, true); } @@ -1444,11 +1521,11 @@ class Options { double KickMagnitude1() const { return OPT_VALUE("kick-magnitude-1", m_KickMagnitude1, true); } double KickMagnitude2() const { return OPT_VALUE("kick-magnitude-2", m_KickMagnitude2, true); } - double KickMagnitudeRandom() const { return OPT_VALUE("kick-magnitude-random", m_KickMagnitudeRandom, true); } - double KickMagnitudeRandom1() const { return OPT_VALUE("kick-magnitude-random-1", m_KickMagnitudeRandom1, true); } - double KickMagnitudeRandom2() const { return OPT_VALUE("kick-magnitude-random-2", m_KickMagnitudeRandom2, true); } + double KickMagnitudeRandom() const { return OPT_VALUE("kick-magnitude-random", m_KickMagnitudeRandom, false); } + double KickMagnitudeRandom1() const { return OPT_VALUE("kick-magnitude-random-1", m_KickMagnitudeRandom1, false); } + double KickMagnitudeRandom2() const { return OPT_VALUE("kick-magnitude-random-2", m_KickMagnitudeRandom2, false); } - std::vector LogClasses() const { return m_CmdLine.optionValues.m_LogClasses; } + STR_VECTOR LogClasses() const { return m_CmdLine.optionValues.m_LogClasses; } std::string LogfileCommonEnvelopes() const { return m_CmdLine.optionValues.m_LogfileCommonEnvelopes; } int LogfileCommonEnvelopesRecordTypes() const { return m_CmdLine.optionValues.m_LogfileCommonEnvelopesRecordTypes; } std::string LogfileDefinitionsFilename() const { return m_CmdLine.optionValues.m_LogfileDefinitionsFilename; } @@ -1488,6 +1565,14 @@ class Options { : std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SWITCH_LOG)) ); } + std::string LogfileSystemSnapshotLog() const { return m_CmdLine.optionValues.m_Populated && !m_CmdLine.optionValues.m_VM["logfile-system-snapshot-log"].defaulted() + ? m_CmdLine.optionValues.m_LogfileSystemSnapshotLog + : (m_CmdLine.optionValues.m_EvolutionMode.type == EVOLUTION_MODE::SSE + ? std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::SSE_SYSTEM_SNAPSHOT_LOG)) + : std::get<0>(LOGFILE_DESCRIPTOR.at(LOGFILE::BSE_SYSTEM_SNAPSHOT_LOG)) + ); + } + int LogfileSystemSnapshotLogRecordTypes() const { return m_CmdLine.optionValues.m_LogfileSystemSnapshotLogRecordTypes; } std::string LogfileSystemParameters() const { return m_CmdLine.optionValues.m_Populated && !m_CmdLine.optionValues.m_VM["logfile-system-parameters"].defaulted() ? m_CmdLine.optionValues.m_LogfileSystemParameters : (m_CmdLine.optionValues.m_EvolutionMode.type == EVOLUTION_MODE::SSE @@ -1496,6 +1581,7 @@ class Options { ); } int LogfileSystemParametersRecordTypes() const { return m_CmdLine.optionValues.m_LogfileSystemParametersRecordTypes; } + LOGFILETYPE LogfileType() const { return m_CmdLine.optionValues.m_LogfileType.type; } std::string LogfileTypeString() const { return m_CmdLine.optionValues.m_LogfileType.typeString; } int LogLevel() const { return m_CmdLine.optionValues.m_LogLevel; } @@ -1506,6 +1592,9 @@ class Options { LBV_MASS_LOSS_PRESCRIPTION LBVMassLossPrescription() const { return OPT_VALUE("LBV-mass-loss-prescription", m_LBVMassLossPrescription.type, true); } CORE_MASS_PRESCRIPTION MainSequenceCoreMassPrescription() const { return OPT_VALUE("main-sequence-core-mass-prescription", m_MainSequenceCoreMassPrescription.type, true); } + + double MaltsevFallback() const { return OPT_VALUE("maltsev-fallback", m_MaltsevFallback, true); } + MALTSEV_MODE MaltsevMode() const { return OPT_VALUE("maltsev-mode", m_MaltsevMode.type, true); } double MassChangeFraction() const { return m_CmdLine.optionValues.m_MassChangeFraction; } @@ -1539,8 +1628,8 @@ class Options { double MassTransferFractionAccreted() const { return OPT_VALUE("mass-transfer-fa", m_MassTransferFractionAccreted, true); } double MassTransferJloss() const { return OPT_VALUE("mass-transfer-jloss", m_MassTransferJloss, true); } - double MassTransferJlossMacLeodLinearFractionDegen() const { return OPT_VALUE("mass-transfer-jloss-macleod-linear-fraction-degen", m_MassTransferJlossMacLeodLinearFractionDegen, true); } - double MassTransferJlossMacLeodLinearFractionNonDegen() const { return OPT_VALUE("mass-transfer-jloss-macleod-linear-fraction-non-degen", m_MassTransferJlossMacLeodLinearFractionNonDegen, true); } + double MassTransferJlossLinearFractionDegen() const { return OPT_VALUE("mass-transfer-jloss-linear-fraction-degen", m_MassTransferJlossLinearFractionDegen, true); } + double MassTransferJlossLinearFractionNonDegen() const { return OPT_VALUE("mass-transfer-jloss-linear-fraction-non-degen", m_MassTransferJlossLinearFractionNonDegen, true); } MT_REJUVENATION_PRESCRIPTION MassTransferRejuvenationPrescription() const { return OPT_VALUE("mass-transfer-rejuvenation-prescription", m_MassTransferRejuvenationPrescription.type, true); } MT_THERMALLY_LIMITED_VARIATION MassTransferThermallyLimitedVariation() const { return OPT_VALUE("mass-transfer-thermal-limit-accretor-multiplier", m_MassTransferThermallyLimitedVariation.type, true); } double MaxEvolutionTime() const { return OPT_VALUE("maximum-evolution-time", m_MaxEvolutionTime, true); } @@ -1554,11 +1643,12 @@ class Options { double MetallicityDistributionMax() const { return OPT_VALUE("metallicity-distribution-max", m_MetallicityDistributionMax, true); } double MetallicityDistributionMin() const { return OPT_VALUE("metallicity-distribution-min", m_MetallicityDistributionMin, true); } - double MinimumMassSecondary() const { return OPT_VALUE("minimum-secondary-mass", m_MinimumMassSecondary, true); } + double MinimumSampledSecondaryMass() const { return OPT_VALUE("minimum-sampled-secondary-mass", m_MinimumSampledSecondaryMass, true); } double MullerMandelKickMultiplierBH() const { return OPT_VALUE("muller-mandel-kick-multiplier-BH", m_MullerMandelKickBH, true); } double MullerMandelKickMultiplierNS() const { return OPT_VALUE("muller-mandel-kick-multiplier-NS", m_MullerMandelKickNS, true); } - double MullerMandelSigmaKick() const { return OPT_VALUE("muller-mandel-sigma-kick", m_MullerMandelSigmaKick, true); } + double MullerMandelSigmaKickBH() const { return OPT_VALUE("muller-mandel-sigma-kick-BH", m_MullerMandelSigmaKickBH, true); } + double MullerMandelSigmaKickNS() const { return OPT_VALUE("muller-mandel-sigma-kick-NS", m_MullerMandelSigmaKickNS, true); } bool NatalKickForPPISN() const { return OPT_VALUE("natal-kick-for-PPISN", m_NatalKickForPPISN, false); } NEUTRINO_MASS_LOSS_PRESCRIPTION NeutrinoMassLossAssumptionBH() const { return OPT_VALUE("neutrino-mass-loss-BH-formation", m_NeutrinoMassLossAssumptionBH.type, true); } @@ -1568,9 +1658,9 @@ class Options { NS_EOS NeutronStarEquationOfState() const { return OPT_VALUE("neutron-star-equation-of-state", m_NeutronStarEquationOfState.type, true); } std::string Notes(const size_t p_Idx) const { return OPT_VALUE("notes", m_Notes[p_Idx], true); } - std::vector Notes() const { return OPT_VALUE("notes", m_Notes, true); } + STR_VECTOR Notes() const { return OPT_VALUE("notes", m_Notes, true); } std::string NotesHdrs(const size_t p_Idx) const { return m_CmdLine.optionValues.m_NotesHdrs[p_Idx]; } - std::vector NotesHdrs() const { return m_CmdLine.optionValues.m_NotesHdrs; } + STR_VECTOR NotesHdrs() const { return m_CmdLine.optionValues.m_NotesHdrs; } size_t nObjectsToEvolve() const { return m_CmdLine.optionValues.m_ObjectsToEvolve; } OB_MASS_LOSS_PRESCRIPTION OBMassLossPrescription() const { return OPT_VALUE("OB-mass-loss-prescription", m_OBMassLossPrescription.type, true); } @@ -1648,7 +1738,7 @@ class Options { double RotationalFrequency2() const { return OPT_VALUE("rotational-frequency-2", m_RotationalFrequency2, true); } RSG_MASS_LOSS_PRESCRIPTION RSGMassLossPrescription() const { return OPT_VALUE("RSG-mass-loss-prescription", m_RSGMassLossPrescription.type, true); } - bool ScaleCHEMassLossWithSurfaceHeliumAbundance() const { return OPT_VALUE("scale-CHE-mass-loss-with-surface-helium-abundance", m_ScaleCHEMassLossWithSurfaceHeliumAbundance, false); } + bool ScaleCHEMassLossWithSurfaceHeliumAbundance() const { return OPT_VALUE("scale-CHE-mass-loss-with-surface-helium-abundance", m_ScaleCHEMassLossWithSurfaceHeliumAbundance, true); } double ScaleTerminalWindVelocityWithMetallicityPower() const { return OPT_VALUE("scale-terminal-wind-velocity-with-metallicity-power", m_ScaleTerminalWindVelocityWithMetallicityPower, true);} double SemiMajorAxis() const { return OPT_VALUE("semi-major-axis", m_SemiMajorAxis, true); } SEMI_MAJOR_AXIS_DISTRIBUTION SemiMajorAxisDistribution() const { return OPT_VALUE("semi-major-axis-distribution", m_SemiMajorAxisDistribution.type, true); } @@ -1664,21 +1754,27 @@ class Options { double SN_Theta1() const { return OPT_VALUE("kick-theta-1", m_KickTheta1, true); } double SN_Theta2() const { return OPT_VALUE("kick-theta-2", m_KickTheta2, true); } + ZETA_PRESCRIPTION StellarZetaPrescription() const { return OPT_VALUE("stellar-zeta-prescription", m_StellarZetaPrescription.type, true); } bool StoreInputFiles() const { return m_CmdLine.optionValues.m_StoreInputFiles; } bool SwitchLog() const { return m_CmdLine.optionValues.m_SwitchLog; } - ZETA_PRESCRIPTION StellarZetaPrescription() const { return OPT_VALUE("stellar-zeta-prescription", m_StellarZetaPrescription.type, true); } + double SystemSnapshotAgeThresholds(const size_t p_Idx) const { return OPT_VALUE("system-snapshot-age-thresholds", m_SystemSnapshotAgeThresholds[p_Idx], true); } + DBL_VECTOR SystemSnapshotAgeThresholds() const { return OPT_VALUE("system-snapshot-age-thresholds", m_SystemSnapshotAgeThresholds, true); } + double SystemSnapshotTimeThresholds(const size_t p_Idx) const { return OPT_VALUE("system-snapshot-time-thresholds", m_SystemSnapshotTimeThresholds[p_Idx], true); } + DBL_VECTOR SystemSnapshotTimeThresholds() const { return OPT_VALUE("system-snapshot-time-thresholds", m_SystemSnapshotTimeThresholds, true); } TIDES_PRESCRIPTION TidesPrescription() const { return OPT_VALUE("tides-prescription", m_TidesPrescription.type, true); } std::string TimestepsFileName() const { return OPT_VALUE("timesteps-filename", m_TimestepsFileName, true); } - double TimestepMultiplier() const { return m_CmdLine.optionValues.m_TimestepMultiplier; } + double TimestepMultiplier() const { return OPT_VALUE("timestep-multiplier", m_TimestepMultiplier, true); } + double TimestepMultipliers(const size_t p_Idx) const { return OPT_VALUE("timestep-multipliers", m_TimestepMultipliers[p_Idx], true); } + DBL_VECTOR TimestepMultipliers() const { return OPT_VALUE("timestep-multipliers", m_TimestepMultipliers, true); } bool UseFixedUK() const { return (m_GridLine.optionValues.m_UseFixedUK || m_CmdLine.optionValues.m_UseFixedUK); } - bool UseMassLoss() const { return OPT_VALUE("use-mass-loss", m_UseMassLoss, true); } bool UseMassTransfer() const { return OPT_VALUE("use-mass-transfer", m_UseMassTransfer, true); } bool UsePairInstabilitySupernovae() const { return OPT_VALUE("pair-instability-supernovae", m_UsePairInstabilitySupernovae, true); } bool UsePulsationalPairInstability() const { return OPT_VALUE("pulsational-pair-instability", m_UsePulsationalPairInstability, true); } + bool USSNKicksOverrideMandelMuller() const { return OPT_VALUE("USSN-kicks-override-mandel-muller", m_USSNKicksOverrideMandelMuller, true); } VMS_MASS_LOSS_PRESCRIPTION VMSMassLossPrescription() const { return OPT_VALUE("VMS-mass-loss-prescription", m_VMSMassLossPrescription.type, true); } double WolfRayetFactor() const { return OPT_VALUE("wolf-rayet-multiplier", m_WolfRayetFactor, true); } diff --git a/src/Remnants.h b/src/Remnants.h index 365152636..d028a881a 100644 --- a/src/Remnants.h +++ b/src/Remnants.h @@ -40,7 +40,12 @@ class Remnants: virtual public BaseStar, public HeGB { double CalculateCoreMassOnPhase() const { return m_Mass; } // Return m_Mass double CalculateCriticalMassRatio(const bool p_AccretorIsDegenerate, - const double p_massTransferEfficiencyBeta) { return 0.0; } // Should never be called... + const double p_massTransferEfficiencyBeta) { return 0.0; } // Should not be called (but if it is, mass transfer from a neutron star always treated as stable) + + double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const { return 0.0; } + double CalculateCriticalMassRatioGeEtAl(const QCRIT_PRESCRIPTION p_qCritPrescription, + const double p_massTransferEfficiencyBeta) { return 0.0; } + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return 0.0; } void CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams) { GiantBranch::CalculateGBParams(p_Mass, p_GBParams); } // Default to GiantBranch void CalculateGBParams() { CalculateGBParams(m_Mass0, m_GBParams); } // Use class member variables @@ -53,8 +58,8 @@ class Remnants: virtual public BaseStar, public HeGB { double CalculateHeCoreMassOnPhase() const { return m_Mass; } // Return m_Mass - DBL_DBL_DBL_DBL CalculateImKlmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { return std::make_tuple(0.0, 0.0, 0.0, 0.0); } // Default is no tidal response - DBL_DBL_DBL_DBL CalculateImKlmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { return std::make_tuple(0.0, 0.0, 0.0, 0.0); } // Default is no tidal response + DBL_DBL_DBL_DBL CalculateImKnmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { return std::make_tuple(0.0, 0.0, 0.0, 0.0); } // Default is no tidal response + DBL_DBL_DBL_DBL CalculateImKnmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) const { return std::make_tuple(0.0, 0.0, 0.0, 0.0); } // Default is no tidal response double CalculateInitialSupernovaMass() const { return GiantBranch::CalculateInitialSupernovaMass(); } // Use GiantBranch @@ -69,7 +74,7 @@ class Remnants: virtual public BaseStar, public HeGB { double CalculateMassLossRateHurley() { m_DominantMassLossRate = MASS_LOSS_TYPE::NONE ; return 0.0; } double CalculateMassLossRateBelczynski2010() { m_DominantMassLossRate = MASS_LOSS_TYPE::NONE ; return 0.0; } - double CalculateMassLossRateMerritt2024() { m_DominantMassLossRate = MASS_LOSS_TYPE::NONE ; return 0.0; } // + double CalculateMassLossRateMerritt2025() { m_DominantMassLossRate = MASS_LOSS_TYPE::NONE ; return 0.0; } // double CalculatePerturbationMuOnPhase() const { return m_Mu; } // NO-OP diff --git a/src/Star.cpp b/src/Star.cpp index 74ccd6583..7631e3890 100644 --- a/src/Star.cpp +++ b/src/Star.cpp @@ -39,6 +39,10 @@ Star::Star(const unsigned long int p_RandomSeed, } m_SaveStar = nullptr; + + // thresholds flags for system detailed output file + if (OPTIONS->SystemSnapshotAgeThresholds().size() > 0) m_SystemSnapshotAgeFlags.assign(OPTIONS->SystemSnapshotAgeThresholds().size(), -1.0); + if (OPTIONS->SystemSnapshotTimeThresholds().size() > 0) m_SystemSnapshotTimeFlags.assign(OPTIONS->SystemSnapshotTimeThresholds().size(), false); } @@ -375,8 +379,6 @@ void Star::EvolveOneTimestep(const double p_Dt) { (void)m_Star->SpinDownIsolatedPulsar(p_Dt * MYR_TO_YEAR * SECONDS_IN_YEAR); // update pulsar parameters due to spin down as an isolated pulsar; convert timestep to seconds for this function (uses cgs units) } - (void)m_Star->PrintStashedSupernovaDetails(); // print stashed SSE Supernova log record if necessary - (void)m_Star->PrintDetailedOutput(m_Id, SSE_DETAILED_RECORD_TYPE::POST_MASS_LOSS); // log record - post mass loss } @@ -449,7 +451,7 @@ EVOLUTION_STATUS Star::Evolve(const long int p_Id) { dt = timesteps[stepNum]; } else { // not using user-provided timesteps - dt = m_Star->CalculateTimestep() * OPTIONS->TimestepMultiplier(); // calculate new timestep + dt = m_Star->CalculateTimestep() * OPTIONS->TimestepMultiplier() * OPTIONS->TimestepMultipliers(static_cast(m_Star->StellarType())); // calculate new timestep dt = std::round(dt / TIMESTEP_QUANTUM) * TIMESTEP_QUANTUM; // quantised } stepNum++; // increment step number @@ -458,7 +460,46 @@ EVOLUTION_STATUS Star::Evolve(const long int p_Id) { UpdateAttributes(0.0, 0.0, true); // keeps SSE in sync with BSE (void)m_Star->PrintDetailedOutput(m_Id, SSE_DETAILED_RECORD_TYPE::TIMESTEP_COMPLETED); // log detailed output record - + + // check thresholds for system detailed output printing + // don't use utils::Compare() here - not for time/age + + bool printSystemSnapshotRec = false; // so we only print this timestep once + + // age threshold + // we print a record each timestep the star crosses the threshold from below + // notes: + // (a) the age of individual stars can drop for various reasons (phase change, rejuvenation, winds/mass transfer, etc.), + // and if the age of the star drops below an age threshold, we will log another record if the star then ages beyond + // the same threshold (so we might log several records for the star crossing the same threshold if the age of the + // star oscillates around the threshold) + // (b) we will print multiple records for exceeding the age threshold if the constituent stars exceed the age threshold + // at different timesteps (likely) + for (size_t threshold = 0; threshold < OPTIONS->SystemSnapshotAgeThresholds().size(); threshold++) { // for each system detailed output age threshold + + double thresholdValue = OPTIONS->SystemSnapshotAgeThresholds(threshold); // this threshold value + + // flag need to print (log) system snapshot record + // we don't want to print multiple records for the same timestep, so we flag need rather than print here + printSystemSnapshotRec |= m_SystemSnapshotAgeFlags[threshold] < 0.0 && m_Star->Age() >= thresholdValue; + + // record the current age of the star in the threshold flag - this is how we check for re-crossing a threshold + // if the age of the star has dropped below the threshold value, we reset the threshold flag for the star + // the check will fail if the star hasn't crossed the threshold already, but the flag will be -1.0 anyway + m_SystemSnapshotAgeFlags[threshold] = (m_Star->Age() < thresholdValue) ? -1.0 : m_Star->Age(); + } + + // time threshold + // we print a record at the first timestep that the simulation time exceeds the time threshold + for (size_t threshold = 0; threshold < OPTIONS->SystemSnapshotTimeThresholds().size(); threshold++) { // for each system snapshott time threshold + if (!m_SystemSnapshotTimeFlags[threshold] && m_Star->Time() >= OPTIONS->SystemSnapshotTimeThresholds(threshold)) { // need to action? + m_SystemSnapshotTimeFlags[threshold] = true; // yes, flag action taken + printSystemSnapshotRec = true; // flag need to print (log) system snapshot record + } + } + + if (printSystemSnapshotRec) (void)m_Star->PrintSystemSnapshotLog(); // print (log) system record record if necessary + if (m_Star->StellarType() == STELLAR_TYPE::NEUTRON_STAR && OPTIONS->EvolvePulsars()){ // Pulsar output if star is a neutron star and user wants pulsar output (void)m_Star->PrintPulsarEvolutionParameters(SSE_PULSAR_RECORD_TYPE::TIMESTEP_COMPLETED); // log pulsar evolution parameters } @@ -470,8 +511,6 @@ EVOLUTION_STATUS Star::Evolve(const long int p_Id) { SHOW_WARN(ERROR::TIMESTEPS_NOT_CONSUMED); // show warning } - (void)m_Star->PrintStashedSupernovaDetails(); // print final stashed SSE Supernova log record if necessary - (void)m_Star->PrintDetailedOutput(m_Id, SSE_DETAILED_RECORD_TYPE::FINAL_STATE); // log detailed output record // if we trapped a floating-point error we set the star's error value to indicate a diff --git a/src/Star.h b/src/Star.h index 25a3656d9..57cc7e388 100755 --- a/src/Star.h +++ b/src/Star.h @@ -97,6 +97,7 @@ class Star { double HydrogenAbundanceSurface() const { return m_Star->HydrogenAbundanceSurface(); } double InitialHeliumAbundance() const { return m_Star->InitialHeliumAbundance(); } double InitialHydrogenAbundance() const { return m_Star->InitialHydrogenAbundance(); } + double InitialMainSequenceCoreMass() const { return m_Star->InitialMainSequenceCoreMass(); } bool IsAIC() const { return m_Star->IsAIC(); } bool IsCCSN() const { return m_Star->IsCCSN(); } bool IsDegenerate() const { return m_Star->IsDegenerate(); } @@ -165,14 +166,14 @@ class Star { const double p_ConvectiveEnvelopeMass, const double p_Radius, const double p_Lambda) { return m_Star->CalculateConvectiveEnvelopeBindingEnergy(p_TotalMass, p_ConvectiveEnvelopeMass, p_Radius, p_Lambda); } - double CalculateConvectiveEnvelopeLambdaPicker(const double p_convectiveEnvelopeMass, const double p_maxConvectiveEnvelopeMass ) const { return m_Star->CalculateConvectiveEnvelopeLambdaPicker(p_convectiveEnvelopeMass, p_maxConvectiveEnvelopeMass); } + double CalculateConvectiveEnvelopeLambdaPicker(const DBL_DBL p_convectiveEnvelopeMass) const { return m_Star->CalculateConvectiveEnvelopeLambdaPicker(p_convectiveEnvelopeMass); } DBL_DBL CalculateConvectiveEnvelopeMass() { return m_Star->CalculateConvectiveEnvelopeMass(); } double CalculateEddyTurnoverTimescale() { return m_Star->CalculateEddyTurnoverTimescale(); } - DBL_DBL_DBL_DBL CalculateImKlmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) { return m_Star->CalculateImKlmDynamical(p_Omega, p_SemiMajorAxis, p_M2); } - DBL_DBL_DBL_DBL CalculateImKlmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2){ return m_Star->CalculateImKlmEquilibrium(p_Omega, p_SemiMajorAxis, p_M2); } - DBL_DBL_DBL_DBL CalculateImKlmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) { return m_Star->CalculateImKlmTidal(p_Omega, p_SemiMajorAxis, p_M2); } + DBL_DBL_DBL_DBL CalculateImKnmDynamical(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) { return m_Star->CalculateImKnmDynamical(p_Omega, p_SemiMajorAxis, p_M2); } + DBL_DBL_DBL_DBL CalculateImKnmEquilibrium(const double p_Omega, const double p_SemiMajorAxis, const double p_M2){ return m_Star->CalculateImKnmEquilibrium(p_Omega, p_SemiMajorAxis, p_M2); } + DBL_DBL_DBL_DBL CalculateImKnmTidal(const double p_Omega, const double p_SemiMajorAxis, const double p_M2) { return m_Star->CalculateImKnmTidal(p_Omega, p_SemiMajorAxis, p_M2); } DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, const double p_AccretorMassRate, @@ -185,6 +186,8 @@ class Star { double CalculateNuclearMassLossRate() { return m_Star->CalculateNuclearMassLossRate(); } + double CalculateRadialExpansionTimescaleDuringMassTransfer() { return m_Star->CalculateRadialExpansionTimescaleDuringMassTransfer(); } + double CalculateRadialExtentConvectiveEnvelope() { return m_Star->CalculateRadialExtentConvectiveEnvelope(); } double CalculateRadiusOnMassChange(double p_dM) { return m_Star->CalculateRadiusOnMassChange(p_dM); } @@ -213,8 +216,8 @@ class Star { void ClearCurrentSNEvent() { m_Star->ClearCurrentSNEvent(); } - ACCRETION_REGIME DetermineAccretionRegime(const bool p_HeRich, - const double p_DonorThermalMassLossRate) { return m_Star->DetermineAccretionRegime(p_HeRich, p_DonorThermalMassLossRate); } + ACCRETION_REGIME DetermineAccretionRegime(const double p_DonorThermalMassLossRate, + const bool p_HeRich) { return m_Star->DetermineAccretionRegime(p_DonorThermalMassLossRate, p_HeRich); } ENVELOPE DetermineEnvelopeType() const { return m_Star->DetermineEnvelopeType(); } @@ -303,6 +306,10 @@ class Star { std::vector m_Timesteps; // timesteps vector - for debugging/testing + // thresholds flags for system snapshot file + DBL_VECTOR m_SystemSnapshotAgeFlags; + BOOL_VECTOR m_SystemSnapshotTimeFlags; + protected: OBJECT_ID m_ObjectId; // instantiated object's unique object id diff --git a/src/TPAGB.cpp b/src/TPAGB.cpp index f3fde9bca..a322b0332 100755 --- a/src/TPAGB.cpp +++ b/src/TPAGB.cpp @@ -978,8 +978,11 @@ double TPAGB::ChooseTimestep(const double p_Time) const { * @return Boolean flag: true if star has gone Supernova, false if not */ bool TPAGB::IsSupernova() const { - // no supernova if CO core mass is too low or helium core mass is too low at base of AGB or the envelope has already been removed - return utils::Compare(m_COCoreMass, m_GBParams[static_cast(GBP::McSN)]) >= 0 && - utils::Compare(CalculateInitialSupernovaMass(), OPTIONS->MCBUR1()) >= 0 && - utils::Compare(m_COCoreMass, m_Mass) < 0; + double snMass = CalculateInitialSupernovaMass(); + bool isCCSN = utils::Compare(m_COCoreMass, CalculateCoreMassAtSupernova_Static(MCH, m_GBParams[static_cast(GBP::McBAGB)])) >= 0 && + utils::Compare(snMass, OPTIONS->MCBUR1()) >= 0 && utils::Compare(m_COCoreMass, m_Mass) < 0; + bool isECSN = utils::Compare(snMass, MCBUR2) < 0 && (!m_MassTransferDonorHistory.empty() || OPTIONS->AllowNonStrippedECSN()) && + utils::Compare(m_COCoreMass, CalculateCoreMassAtSupernova_Static(MECS, m_GBParams[static_cast(GBP::McBAGB)])) >= 0 && + utils::Compare(snMass, OPTIONS->MCBUR1()) >= 0 && utils::Compare(m_COCoreMass, m_Mass) < 0; + return isCCSN || isECSN; } diff --git a/src/TPAGB.h b/src/TPAGB.h index d979ff9fc..5906770b5 100755 --- a/src/TPAGB.h +++ b/src/TPAGB.h @@ -48,7 +48,7 @@ class TPAGB: virtual public BaseStar, public EAGB { // member functions - alphabetically DBL_DBL CalculateConvectiveEnvelopeMass() const { return std::tuple (m_Mass-m_CoreMass, m_Mass-m_CoreMass); } // assume entire envelope is convective for TPAGB stars - double CalculateCOCoreMassAtPhaseEnd() const { return (utils::Compare(m_COCoreMass, m_GBParams[static_cast(GBP::McSN)]) >= 0 && utils::Compare(m_COCoreMass, m_Mass) < 0) ? m_COCoreMass : m_Mass; } + double CalculateCOCoreMassAtPhaseEnd() const { return (utils::Compare(m_COCoreMass, m_Mass) < 0) ? m_COCoreMass : m_Mass; } double CalculateCOCoreMassOnPhase() const { return CalculateCoreMassOnPhase(m_Mass0, m_Age); } // McCO(TPAGB) = Mc(TPAGB)Same as on phase double CalculateConvectiveCoreRadius() const { return std::min(5.0 * CalculateRemnantRadius(), m_Radius); } // Last paragraph of section 6 of Hurley+ 2000 @@ -103,7 +103,7 @@ class TPAGB: virtual public BaseStar, public EAGB { void ResolveHeliumFlash() { } // NO-OP STELLAR_TYPE ResolveSkippedPhase() { return m_StellarType; } // NO-OP - bool ShouldEvolveOnPhase() const { return ((utils::Compare(m_COCoreMass, std::min(m_GBParams[static_cast(GBP::McSN)], m_Mass)) < 0) && !ShouldEnvelopeBeExpelledByPulsations()); } // Evolve on TPAGB phase if envelope is not lost and not going supernova + bool ShouldEvolveOnPhase() const { return (utils::Compare(m_COCoreMass, m_Mass) < 0 && !IsSupernova() && !ShouldEnvelopeBeExpelledByPulsations()); } // Evolve on TPAGB phase if envelope is not lost and not going supernova bool ShouldSkipPhase() const { return false; } // Never skip TPAGB phase }; diff --git a/src/WhiteDwarfs.cpp b/src/WhiteDwarfs.cpp index 494647678..a61ac02d9 100644 --- a/src/WhiteDwarfs.cpp +++ b/src/WhiteDwarfs.cpp @@ -1,26 +1,28 @@ #include "WhiteDwarfs.h" #include "NS.h" + + /* Calculate eta_hydrogen from Claeys+ 2014, appendix B. This parameter depends * on three regimes for the mass transfer rate, which here are distinguished by the * thresholds logMdotUppH and logMdotLowH. In Claeys+ 2014, the mass transfer rate is * \dot{M}_{tr} and the thresholds are \dot{M}_{cr,H} and \dot{M}_{cr,H}/8, respectively. * - * However, we have used improved thresholds from Nomoto+ 2007, in which the + * However, we have used improved thresholds from Nomoto+ 2007 in which the * lower boundary is \dot{M}_{stable} and the upper boundary is \dot{M}_{RG}. * More precisely, we implemented quadratic fits to the values in Nomoto+ 2007, - * table 5, as described in Rodriguez+ (in prep). + * table 5, as described in the second COMPAS methods paper (in prep). * * double CalculateEtaH(const double p_MassTransferRate) * - * @param [IN] p_MassTransferRate Mass transfer rate onto the WD surface (Msun/yr) + * @param [IN] p_MassTransferRate Mass transfer rate onto the WD surface (Msun/Myr) * @return Hydrogen accretion efficiency */ double WhiteDwarfs::CalculateEtaH(const double p_MassTransferRate) { double etaH = 0.0; // default return value - double logMassTransferRate = log10(p_MassTransferRate); + double logMassTransferRate = log10(p_MassTransferRate / MYR_TO_YEAR); double m_Mass_2 = m_Mass * m_Mass; // The following coefficients come from quadratic fits to Nomoto+ 2007 results (table 5) in Mass vs log10 Mdot space, to cover the low-mass end. @@ -52,14 +54,14 @@ double WhiteDwarfs::CalculateEtaH(const double p_MassTransferRate) { * * double CalculateEtaHe(const double p_MassTransferRate) * - * @param [IN] p_MassTransferRate Mass transfer rate onto the WD surface (Msun/yr) + * @param [IN] p_MassTransferRate Mass transfer rate onto the WD surface (Msun/Myr) * @return Helium accretion efficiency */ double WhiteDwarfs::CalculateEtaHe(const double p_MassTransferRate) { double etaHe = 1.0; // default return value - so we can have double detonations - double logMassTransferRate = log10(p_MassTransferRate); + double logMassTransferRate = log10(p_MassTransferRate / MYR_TO_YEAR); // The following coefficients in massTransfer limits come from table A1 in Piersanti+ 2014. double logMdotUppHe = WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_0 + WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_1 * m_Mass; @@ -81,42 +83,43 @@ double WhiteDwarfs::CalculateEtaHe(const double p_MassTransferRate) { /* Calculate accretion efficiency as indicated in Piersanti+ 2014, section A3. Their recipe works - * for specific mass and Mdot values, so a better implementation requires interpolation and + * for specific mass and Mdot values, so a better implementation would require interpolation and * extrapolation (specially towards the low-mass end). Right now, we just adopt a * piece-wise approach. Note that the authors also specify that this is based on the first * strong flash only, but we use it for all episodes. * * double CalculateEtaPTY(const double p_MassTransferRate) * - * @param [IN] p_MassTransferRate Mass transfer rate onto the WD surface (Msun/yr) + * @param [IN] p_MassTransferRate Mass transfer rate onto the WD surface (Msun/Myr) * @return Accretion efficiency during the first stron helium flash, Piersanti+ 2014 */ double WhiteDwarfs::CalculateEtaPTY(const double p_MassTransferRate) { - double etaPTY = 0.0; // default return value + double etaPTY = 0.0; // default return value - double massRate = p_MassTransferRate; + double massRate = p_MassTransferRate * 100; // Piersanti + 2014 assumes the rate is in units of 10^-8 Msun / yr (see Eq. A3) double massRate_2 = massRate * massRate; double massRate_3 = massRate_2 * massRate; // Limits on each conditional statement come from masses from each model in Piersanti+ 2014. The final etaPTY value is based on table A3. if (utils::Compare(m_Mass, 0.6) <= 0) { - etaPTY = 6.0e-3 + 5.1e-2 * massRate + 8.3e-3 * massRate_2 - 3.317e-4 * massRate_3; + etaPTY = WD_PIERSANTI_M060_G0 + WD_PIERSANTI_M060_G1 * massRate + WD_PIERSANTI_M060_G2 * massRate_2 - WD_PIERSANTI_M060_G3 * massRate_3; } else if (utils::Compare(m_Mass, 0.7) <= 0) { - etaPTY = -3.5e-2 + 7.5e-2 * massRate - 1.8e-3 * massRate_2 + 3.266e-5 * massRate_3; + etaPTY = -WD_PIERSANTI_M070_G0 + WD_PIERSANTI_M070_G1 * massRate - WD_PIERSANTI_M070_G2 * massRate_2 + WD_PIERSANTI_M070_G3 * massRate_3; } else if (utils::Compare(m_Mass, 0.81) <= 0) { - etaPTY = 9.3e-2 + 1.8e-2 * massRate + 1.6e-3 * massRate_2 - 4.111e-5 * massRate_3; + etaPTY = WD_PIERSANTI_M081_G0 + WD_PIERSANTI_M081_G1 * massRate + WD_PIERSANTI_M081_G2 * massRate_2 - WD_PIERSANTI_M081_G3 * massRate_3; } else if (utils::Compare(m_Mass, 0.92) <= 0) { - etaPTY = -7.59e-2 + 1.54e-2 * massRate + 4.0e-4 * massRate_2 - 5.905e-6 * massRate_3; + etaPTY = -WD_PIERSANTI_M092_G0 + WD_PIERSANTI_M092_G1 * massRate + WD_PIERSANTI_M092_G2 * massRate_2 - WD_PIERSANTI_M092_G3 * massRate_3; } else { - etaPTY = -0.323 + 4.1e-2 * massRate - 7.0e-4 * massRate_2 + 4.733e-6 * massRate_3; + etaPTY = -WD_PIERSANTI_M102_G0 + WD_PIERSANTI_M102_G1 * massRate - WD_PIERSANTI_M102_G2 * massRate_2 + WD_PIERSANTI_M102_G3 * massRate_3; } - return etaPTY; + // Returned eta should be between 0 and 1 + return std::min(std::max(etaPTY, 0.0), 1.0); } @@ -138,12 +141,46 @@ double WhiteDwarfs::CalculateLuminosityOnPhase_Static(const double p_Mass, const return (635.0 * p_Mass * PPOW(p_Metallicity, 0.4)) / PPOW(p_BaryonNumber * (p_Time + 0.1), 1.4); } +/* Calculate: + * + * (a) the maximum mass acceptance rate of this star, as the accretor, during mass transfer, and + * (b) the retention efficiency parameter + * + * Currently used for COWDs and ONeWDs + * + * For a given mass transfer rate, this function computes the amount of mass a WD would retain after + * flashes, as given by appendix B of Claeys+ 2014. + * https://ui.adsabs.harvard.edu/abs/2014A%26A...563A..83C/abstract + * + * + * DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, const bool p_IsHeRich) + * + * @param [IN] p_DonorMassRate Mass transfer rate from the donor + * @param [IN] p_IsHeRich Material is He-rich or not + * @return Tuple containing the Maximum Mass Acceptance Rate (Msun/yr) and Retention Efficiency Parameter + */ +DBL_DBL WhiteDwarfs::CalculateMassAcceptanceRate(const double p_DonorMassRate, const bool p_IsHeRich) { + + m_AccretionRegime = DetermineAccretionRegime(p_DonorMassRate, p_IsHeRich); + + double acceptanceRate = 0.0; // acceptance mass rate - default = 0.0 + double fractionAccreted = 0.0; // accretion fraction - default = 0.0 + + acceptanceRate = p_DonorMassRate * CalculateEtaHe(p_DonorMassRate); + if (!p_IsHeRich) acceptanceRate *= CalculateEtaH(p_DonorMassRate); + + fractionAccreted = acceptanceRate / p_DonorMassRate; + + return std::make_tuple(acceptanceRate, fractionAccreted); +} /* * Calculate the radius of a white dwarf - good for all types of WD * - * Hurley et al. 2000, eq 91 (from Tout et al. 1997) - * + * Originally from Eggleton 1986, quoted in Verbunt & Rappaport 1988 and Marsh et al. 2004 (eq. 24). + * Compared to the Hurley et al. 2000 prescription, the additional factor that includes WD_MP allows + * for the change to a constant density configuration at low masses (e.g., Zapolsky & Salpeter 1969) + * after mass loss episodes. * * double CalculateRadiusOnPhase_Static(const double p_Mass) * @@ -157,10 +194,84 @@ double WhiteDwarfs::CalculateRadiusOnPhase_Static(const double p_Mass) { if (utils::Compare(p_Mass, MCH) >= 0) return NEUTRON_STAR_RADIUS; // only expected to come up if asking for the core or remnant radius of a giant star - double MCH_Mass_one_third = std::cbrt(MCH / p_Mass); - double MCH_Mass_two_thirds = MCH_Mass_one_third * MCH_Mass_one_third; + const double MCH_Mass_one_third = std::cbrt(MCH / p_Mass); + const double MCH_Mass_two_thirds = MCH_Mass_one_third * MCH_Mass_one_third; + + double MP_Mass = WD_MP / p_Mass; + double MP_Mass_two_thirds = MP_Mass / std::cbrt(WD_MP / p_Mass); + + double firstFactor = std::sqrt((MCH_Mass_two_thirds - 1.0 / MCH_Mass_two_thirds)); + double preSecondFactor = 1.0 + 3.5 * MP_Mass_two_thirds + MP_Mass; + double secondFactor = std::cbrt(preSecondFactor) / preSecondFactor; + + return std::max(NEUTRON_STAR_RADIUS, 0.0114 * firstFactor * secondFactor); +} + + +/* + * Determine the WD accretion regime based on the MT rate and whether the donor is He rich. Also, + * initialize He-Shell detonation or Off-center ignition when necessary, by changing the value + * of m_HeShellDetonation or m_OffCenterIgnition (respectively). + * + * The accretion regime is one of the options listed in enum ACCRETION_REGIME (constants.h) + * + * Note that we have merged the different flashes regimes from Piersanti+ 2014 into a single regime. + * + * ACCRETION_REGIME DetermineAccretionRegime(const double p_DonorMassLossRate, const bool p_HeRich) + * + * @param [IN] p_DonorMassLossRate Donor mass loss rate, in units of Msol / Myr + * @param [IN] p_HeRich Whether the accreted material is helium-rich or not + * @return Current WD accretion regime + */ +ACCRETION_REGIME WhiteDwarfs::DetermineAccretionRegime(const double p_DonorMassLossRate, const bool p_HeRich) { + + double logMdot = log10(p_DonorMassLossRate / MYR_TO_YEAR); // logarithm of the accreted mass (M_sun/yr) + ACCRETION_REGIME regime = ACCRETION_REGIME::ZERO; + + if (p_HeRich) { + // The following coefficients in logMassTransfer limits come from table A1 in Piersanti+ 2014. + double logMassTransferCrit = WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_0 + WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_1 * m_Mass; + double logMassTransferStable = WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_0 + WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_1 * m_Mass; // Piersanti+2014 has several Flashes regimes. Here we group them into one. + double logMassTransferDetonation = WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_0 + WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_1 * m_Mass; // critical value for double detonation regime in Piersanti+ 2014 + if (utils::Compare(logMdot, logMassTransferStable) < 0) { + if (utils::Compare(logMdot, logMassTransferDetonation) > 0) { + regime = ACCRETION_REGIME::HELIUM_FLASHES; + } + else { + regime = ACCRETION_REGIME::HELIUM_ACCUMULATION; + if ((utils::Compare(m_Mass, MASS_DOUBLE_DETONATION_CO) >= 0) && (utils::Compare(m_HeShell, WD_HE_SHELL_MCRIT_DETONATION) >= 0)) { + m_HeShellDetonation = true; + } + } + } + else if (utils::Compare(logMdot, logMassTransferCrit) > 0) { + regime = ACCRETION_REGIME::HELIUM_OPT_THICK_WINDS; + } + else { + regime = ACCRETION_REGIME::HELIUM_STABLE_BURNING; + if ((utils::Compare(logMdot, COWD_LOG_MDOT_MIN_OFF_CENTER_IGNITION) > 0) && (utils::Compare(m_Mass, COWD_MASS_MIN_OFF_CENTER_IGNITION) > 0)) { + m_OffCenterIgnition = true; + } + } + } + else { + // The following coefficients in logMassTransfer limits come from quadratic fits to Nomoto+ 2007 results (table 5) in Mass vs log10 Mdot space, to cover the low-mass end. + double m_Mass_2 = m_Mass * m_Mass; + double logMassTransferCrit = WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_0 + WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_1 * m_Mass + WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_2 * m_Mass_2; + double logMassTransferStable = WD_LOG_MT_LIMIT_NOMOTO_STABLE_0 + WD_LOG_MT_LIMIT_NOMOTO_STABLE_1 * m_Mass + WD_LOG_MT_LIMIT_NOMOTO_STABLE_2 * m_Mass_2; + + if (utils::Compare(logMdot, logMassTransferStable) < 0) { + regime = ACCRETION_REGIME::HYDROGEN_FLASHES; + } + else if (utils::Compare(logMdot, logMassTransferCrit) > 0) { + regime = ACCRETION_REGIME::HYDROGEN_OPT_THICK_WINDS; + } + else { + regime = ACCRETION_REGIME::HYDROGEN_STABLE_BURNING; + } + } - return std::max(NEUTRON_STAR_RADIUS, 0.0115 * std::sqrt((MCH_Mass_two_thirds - 1.0 / MCH_Mass_two_thirds))); + return regime; } diff --git a/src/WhiteDwarfs.h b/src/WhiteDwarfs.h index 266c9ff93..4868a10b6 100644 --- a/src/WhiteDwarfs.h +++ b/src/WhiteDwarfs.h @@ -31,7 +31,8 @@ class WhiteDwarfs: virtual public BaseStar, public Remnants { MT_CASE DetermineMassTransferTypeAsDonor() const { return MT_CASE::OTHER; } // Not A, B, C, or NONE - + ACCRETION_REGIME DetermineAccretionRegime(const double p_DonorThermalMassLossRate, const bool p_HeRich); // Get the current accretion regime. Can also change m_HeShellDetonation and m_OffCenterIgnition flags. + void ResolveShellChange(const double p_AccretedMass); @@ -54,6 +55,13 @@ class WhiteDwarfs: virtual public BaseStar, public Remnants { const bool p_DonorIsGiant, const double p_DonorThermalMassLossRate, const double p_MassLostByDonor); + + double CalculateCriticalMassRatio(const bool p_AccretorIsDegenerate, + const double p_massTransferEfficiencyBeta) { return CalculateCriticalMassRatioHurleyHjellmingWebbink(); } + double CalculateCriticalMassRatioClaeys14(const bool p_AccretorIsDegenerate) const { return CalculateCriticalMassRatioHurleyHjellmingWebbink(); } + double CalculateCriticalMassRatioGeEtAl(const QCRIT_PRESCRIPTION p_qCritPrescription, + const double p_massTransferEfficiencyBeta) { return CalculateCriticalMassRatioHurleyHjellmingWebbink(); } + double CalculateCriticalMassRatioHurleyHjellmingWebbink() const { return HURLEY_HJELLMING_WEBBINK_QCRIT_WD; } double CalculateCOCoreMassOnPhase() const { return m_COCoreMass; } // NO-OP @@ -71,10 +79,10 @@ class WhiteDwarfs: virtual public BaseStar, public Remnants { double CalculateEtaPTY(const double p_MassIntakeRate); - double Calculatel0Ritter() const { return (m_Metallicity > 0.01) ? 1995262.3 : 31622.8; } // Luminosity constant which depends on metallicity in Ritter 1999, eq 10 + double Calculatel0Ritter() const { return (m_Metallicity > 0.01) ? L0_RITTER_HIGH_Z : L0_RITTER_LOW_Z; } - virtual DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, - const bool p_IsHeRich) { return std::make_tuple(0.0, 0.0); } + DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, + const bool p_IsHeRich); DBL_DBL CalculateMassAcceptanceRate(const double p_DonorMassRate, const double p_AccretorMassRate, const bool p_IsHeRich) { return CalculateMassAcceptanceRate(p_DonorMassRate, p_IsHeRich); } diff --git a/src/changelog.h b/src/changelog.h index 2eaa4901b..dd6524943 100644 --- a/src/changelog.h +++ b/src/changelog.h @@ -944,10 +944,10 @@ // - Cleaned up stability check functions in BaseBinaryStar.cpp for clarity, and to allow for critical mass ratios to be checked correctly // 02.33.01 RTW - Sep 26, 2022 - Defect repair: // - Fixed interpolation of MACLEOD_LINEAR gamma for specific angular momentum. Previously interpolated on the gamma value, now interpolates in orbital separation -// 02.33.02 IM - Nov 27, 2022 - Defect repair: +// 02.33.02 IM - Nov 27, 2022 - Defect repair: // - Fixed ignored value of input radius when computing the thermal timescale, relevant if using Roche lobe radius instead (issue #853) // - Cleaned code and comments around the use of MT_THERMALLY_LIMITED_VARIATION::RADIUS_TO_ROCHELOBE vs. C_FACTOR (issue #850) -// 02.34.00 IM - Nov 28, 2022 - Enhancement: +// 02.34.00 IM - Nov 28, 2022 - Enhancement: // - Adding framework for Hirai & Mandel 2-stage common envelope formalism // (placeholders for now -- will have identical results to default version) // - Placed Dewi CE prescription on parity with others @@ -1521,7 +1521,183 @@ // alternatively, with TRANSFER_TO_ORBIT variation, the star continues to accrete, but excess angular momentum is deposited in the orbit // - Fixed problem in options code where including "--option-name" in option descriptions sometimes caused YAML file defaults to be parsed incorrectly // - Added OMEGA and OMEGA_BREAK to SSE detailed output (to address #243) +// 03.17.01 VK - Apr 7, 2025 - Defect Repair: +// - Fix for issue #1365 - Converted user-specified initial rotational frequency from cycles/yr to rad/yr. +// 03.17.02 JR - Apr 11, 2025 - Defect Repair: +// - Remove extraneous debug print statement in MainSequence.h (inavertently added by me in v03.17.00) +// - fix description of return value for BaseStar::CalculateOmegaCHE() +// 03.17.03 YS - Apr 14, 2025 - Enhancement: +// - Fix to issue #1366 +// - Removed "RLOF_ONTO_NS" output option as it can be retrieved from RLOF Output info. +// 03.17.04 AB - Apr 14, 2025 - Defect repair, Enhancement: +// - Fixes and enhancements to BRCEK core mass prescription: core mass now never reaches the total mass, radius correctly follows the RL radius +// during nuclear timescale mass transfer, and added functionality to track surface helium abundance on the MS +// - MainSequence::CalculateRadiusOnPhase() and CalculateRadiusOnPhaseTau() were combined into one function +// - Limit time step during nuclear timescale mass transfer +// 03.18.00 JR - Apr 14, 2025 - Enhancement: +// - Add option "--timestep-multipliers" to enable more granular, phase-dependent, timestep multipliers (see documentation for use) +// - Added maximum allowed value for options `--timestep-multiplier` and `--timestep-multipliers` +// 03.18.01 AB - Apr 28, 2025 - Defect repair: +// - Updated coefficients from Shikauchi et al. (2024) for main-sequence core mass calculations +// - Fixed behaviour of surface helium abundance for HeMS stars and core helium abundance for HeHG stars +// 03.18.02 IM - May 1, 2025 - Enhancement: +// - Added several outputs to the BSE and SSE Switch logs necessary for inter-run post-processing comparisons +// - Changed the default behaviour to use enhanced Nanjing lambdas (for common envelope calculations), interpolating in mass and metallicity +// 03.18.03 IM - May 2, 2025 - Defect repair: +// - Fix for issue #1380, which appears when the Loveridge binding energy is so high that lambda is rounded off to zero +// 03.18.04 IM - May 4, 2025 - Defect repair: +// - Added a check to avoid Loveridge lambda becoming zero when the envelope mass is positive but very small +// 03.18.05 JR - May 8, 2025 - Defect repair: +// - Fix for issue #1378: reinstate "false" fallback option for `kick-magnitude-random*` options (mistakenly changed to "true" in v03.00.00) +// 03.18.06 IM - May 9, 2025 - Enhancement, defect repair: +// - Added several outputs to provide more information and aid code testing: SEMI_MAJOR_AXIS_POST_STAGE_1_CE (for the 2-stage CE); BINDING_ENERGY_CONVECTIVE_ENVELOPE, CONVECTIVE_ENV_MASS, LAMBDA_CONVECTIVE_ENVELOPE (convective envelope binding energy, mass, and lambda, respectively, Picker prescription) +// - Ensure that the MANDEL main sequence core mass prescription never provides a core mass exceeding the total mass +// - Corrected several erroneous comments, cleaned up a few function definitions +// 03.18.07 IM - May 11, 2025 - Defect repair: +// - Corrected and simplified GiantBranch::CalculateRadialExtentConvectiveEnvelope() +// 03.19.00 JR - May 21, 2025 - Enhancement: +// - Added functionality to create new System Snapshot logfile +// Writing to the System Snapshot logfile is triggered by system age and/or simulation time passing thresholds set +// by new program options (see below). New program options added: +// --logfile-system-snapshot-log: specifies the name of the System Snapshot logfile (default is "[BSE/SSE]_System_Snapshot_Log") +// --logfile-system-snapshot-log-record-types: specifies the enabled record types for System Snapshot logfile (default is all types) +// --system-snapshot-age-thresholds: specifies the age thresholds for System Snapshot logfile +// --system-snapshot-time-thresholds: specifies the time thresholds for System Snapshot logfile +// - Removed stashing to SSE SNe file - printing is now done directly (stashing no longer required since we now don't save/revert). +// I was going to remove the save/revert state functions - we don't need them (we have clone functions), but I left them in for now (just in case we find a need...). +// 03.20.00 VK - May 25, 2025 - Enhancement, Defect repair: +// - Replaced the name of the KAPIL2024 tides presctiption with KAPIL2025 to match the reference. +// - Updated equilibrium and dynamical tides to be consistent with paper. Most notably, corrected all tidal terms to have l=2, and updated the other indices to n and m. +// - Added variables for circularization timescale, synchronization timescales (for both stars), and all the tidal ImKnm potential Love numbers to BSE output. +// 03.20.01 SS/NRS - May 26, 2025 - Enhancement: +// - Improvements to mass accretion for massive ONe WDs +// - Added ONe::CalculateMassAcceptanceRate +// - Fix units of logMassTransferRate in WhiteDwarfs::CalculateEtaHe and WhiteDwarfs::CalculateEtaH +// - Update white dwarf mass-radius relation (WhiteDwarfs::CalculateRadiusOnPhase_Static) +// - Moved white dwarf related constants to constants.h (resolves issue #1351) +// - Set merger on unstable RLOF from WD +// 03.20.02 IM - May 30, 2025 - Defect repair, enhancement: +// - Included unit conversion in WhiteDwarfs::CalculateEtaPTY() +// - All critical mass ratios now return the HURLEY_HJELLMING_WEBBINK_QCRIT_WD for white dwarfs and 0 for other remnant donors (only stable mass transfer) as fix for issue #1385 +// 03.20.03 IM - June 18, 2025 - Defect repair, enhancement: +// - TPAGB stars should no longer experience supernovae if SN conditions are not satisfied, rather than defaulting to CCSN (corrects the partial fix in 03.10.02) +// - Added new parameter (threshold mass, generally expected to be MCH or MECS) to CalculateCoreMassAtSupernova_Static() +// - Removed McSN from GBParams, instead computed on the fly when needed +// 03.20.04 AB - Jun 23, 2025 - Defect repair, enhancement: +// - Fixes to MS mergers and CHE when BRCEK core mass prescription is used -- MS core mass is now correctly initialised after full mixing in MS +// mergers and CH stars that spun down +// 03.20.05 IM - June 24, 2025 - Defect repair: +// - Fixed typo in implementation of CASE_BB_STABILITY_PRESCRIPTION::ALWAYS_STABLE_ONTO_NSBH (issue #1403) +// 03.20.06 IM - June 25, 2025 - Enhancements: +// - The MAXWELLIAN NS CCSN kick changed from the Hobbs value of 265 km/s to 217 km/s based on 48 younger than 10 Myr pulsars with proper motions from Disberg & Mandel (2025) sample; corrects Hobbs+ 2005 missing Jacobian +// - Implemented a LOGNORMAL NS CCSN kick magnitude distribution based on Disberg & Mandel, 2025 +// 03.20.07 IM - June 25, 2025 - Enhancement: +// - Added a maximum threshold of 1000 km/s for Disberg & Mandel (2025) LOGNORMAL kicks, matching paper +// 03.20.08 AB - Jun 26, 2025 - Defect repair: +// - Fix for issue #400; correct Zsol values are now used in stellar wind prescriptions +// - To avoid ambiguous ZSOL, we now use ZSOL_HURLEY = 0.02, ZSOL_ANDERS = 0.019, and ZSOL_ASPLUND = 0.0142 +// - Fixed error in MainSequence::CalculateInitialMainSequenceCoreMass() +// 03.20.09 RTW - Jun 30, 2025 - Enhancement: +// - Added individual velocity components for stars to the LogTypedefs file so they can be included in the output (as ANY_STAR_PROPERTY::VELOCITY_X, or Y, Z) +// 03.21.00 JR - Jul 17, 2025 - Enhancement: +// - Changed mass loss prescription MERRITT2024 to MERRIT2025; deprecated MERRIT2024 +// - Added datestrings to vectors supporting deprecation in Options.h to allow timely removal +// - Added version strings for gsl, boost, and HDF5 to COMPAS splashscreen (for now, gsl & hdf5 are installed versions, boost is version compiled with COMPAS) +// 03.22.00 IM - July 17, 2025 - Enhancements, defect repair: +// - Changed default values of --enhance-CHE-lifetimes-luminosities and --scale-CHE-mass-loss-with-surface-helium-abundance to true +// - Added options to set beta and gamma prescription for second stage of 2-stage CE (--common-envelope-second-stage-beta, --common-envelope-second-stage-gamma-prescription) +// - Fixed a bug in CalculateZetaEquilibrium(), which impacted when mass transfer is declared nuclear (and how conservative it is) +// - Added missing virtual declaration to ShouldEnvelopeBeExpelledByPulsations +// - Now calculate mass accretion rate for nuclear timescale mass transfer on the fly to match with donor mass loss rate set by donor mass loss (required to fit into Roche lobe) divided by time step +// - Fixed random draws of SN kicks to avoid artificial pile-up at boundaries of distribution +// - Split --muller-mandel-sigma-kick into --muller-mandel-sigma-kick-NS and --muller-mandel-sigma-kick-BH +// 03.22.01 IM - July 20, 2025 - Defect repair: +// - Fixed random kick draw for MULLERMANDEL prescription +// 03.22.02 JR - August 08, 2025 - Defect repair/enhancement (~cleanups for consistency): +// - Changes for issue 1413: +// - Deprecated options +// . "--minimum-mass-secondary" in favour of "--minimum-sampled-secondary-mass" +// . "--initial-mass-min" in favour of "--initial-mass-function-min" +// . "--initial-mass-max" in favour of "--initial-mass-function-max" +// . "--initial-mass-power" in favour of "--initial-mass-function-power" +// - Default value for "--initial-mass-function-min" remains at 5.0 Msol +// - Default value for "--initial-mass-function-max" remains at 150.0 Msol +// - Default value for "--initial-mass-function-power" remains at 0.0 +// - Default value for "--minimum-sampled-secondary-mass" remains at 0.1 Msol +// - User supplied value for "--minimum-sampled-secondary-mass" checked against values of constants MINIMUM_INITIAL_MASS and MINIMUM_INITIAL_MASS +// - Secondary mass, whether input by user, sampled, or calculated from M1 & q, checked against constant MINIMUM-INITIAL-MASS +// - Online docs "program-options-list-defaults" and "standard-logfiles-record-specification-options" pages updated +// - Whatsnew page updated +// - Changes for issue 1414: +// - Changed [SSE/BSE]_PULSAR_RECORDTYPE::DEFAULT to [SSE/BSE]_PULSAR_RECORDTYPE::PRE_SN +// - don't want DEFAULT anymore, but wanted to preserve numbering. Neither PRE_SN nor POST_SN are currently used (POST_SN was), so we could rename them one day. +// - Removed default parameter from BaseStar::PrintPulsarEvolutionParameters() and BaseBinaryStar::PrintPulsarEvolutionParameters() +// - Changed default record types for: +// - SSE and BSE detailed output files to include only record types 1, 4, & 5 (INITIAL_STATE, TIMESTEP_COMPLETED, and FINAL_STATE) +// - SSE and BSE pulsar evolution files to include only record type 3 ((pulsar) TIMESTEP_COMPLETED) +// 03.23.00 JR - August 09, 2025 - Enhancement: +// - Deprecated option +// - "--use-mass-loss" in favour of "--mass-loss-prescription" +// +// Instead of using ``--use-mass-loss`` or ``--use-mass-loss true`` to enable mass loss, then specifying the mass loss +// prescription to be used with ``--mass-loss-prescription``, mass loss can be enabled using ``--mass-loss-prescription`` +// with any valid prescription (that is not ``zero``), and disabled with ``--mass-loss-prescription zero`` instead of +// ``use-mass-loss false``. +// - Added compiler flag "-Wno-vla-cxx-extension" to "CXXFLAGS" in Makefile to suppress compiler extension warning +// - Fixed online docs for omissions in v03.22.02: +// - fixed description for "--initial-mass-function" in "program-options-list-defaults.rst", and +// - changed "--initial-mass-power" to "--initial-mass-function-power" in "program-options-list-defaults.rst" +// 03.23.01 IM - August 18, 2025 - Enhancement, defect repair: +// - In the MALTSEV SN prescription, treat wind-stripped stars as if they experienced case B mass transfer +// - Limit the output of CalculateEtaPTY() [Helium accretion efficiency onto WDs from Piersanti+ 2014, A3] to be in [0,1] +// 03.24.00 RTW - August 18, 2025 - Enhancement: +// - Updated Maltsev SN prescription, to include Maltsev mode (extrapolation variant outside of Z bounds), +// fallback option, fixed remnant mass, and added lum and teff as attributes of RLOFProperties +// 03.25.00 RTW - August 18, 2025 - Enhancement: +// - Added KLENCKI_LINEAR AM loss, which is linear in the specific AM gamma instead of the orbital separation (as in MACLEOD_LINEAR) +// 03.25.01 JR - August 20, 2025 - Defect repairs: +// - Corrected calculations for Hurley A(n) and B(n) coefficients (see Hurley et al. 2000, appendix) +// - Changed utils::GetGSLVersion() to avoid compiler warning "warning: ignoring attributes on template argument ‘int (*)(FILE*)’ [-Wignored-attributes]" +// - Reverted Makefile line "SOURCES := $(wildcard *.cpp)" to listing actual source files +// 03.25.02 JR - August 20, 2025 - Defect repairs: +// - Clamped B_GAMMA to [0.0, B_GAMMA] (per discussion just after eq 23 - confirmed in BSE Fortran source) +// - Corrected calculation for Hurley Gamma constant C (C_GAMMA - see Hurley et al. 2000, just after eq 23, should use a(75) <= 1.0, not a(75) == 1.0 - confirmed in BSE Fortran source) +// - Added abs() to gamma calculation in Mainsequence.cpp::CalculateGamma() (per BSE Fortran source) +// - Clamped gamma to [0.0, gamma] in Mainsequence.cpp::CalculateGamma() (per discussion just after eq 23 - confirmed in BSE Fortran source) +// 03.26.00 IM - September 2, 2025 - Enhancement, defect repairs: +// - First (simplified) implementation of the Lau+ (2024) Hamstars thermally limited accretion prescription +// - Corrected errors in combining OB and WR winds in CH::CalculateMassLossRateBelczynski2010(), CalculateMassLossRateMerritt2025() and CH::CalculateMassLossFractionOB() [previously CalculateMassLossRateWeightOB()] +// 03.26.01 AB - October 24, 2025 - Option name change: +// - Main sequence core mass prescription ZERO renamed to HURLEY; deprecated ZERO +// 03.26.02 IM - October 27, 2025 - Enhancements +// - Added option --USSN-kicks-override-mandel-muller ; if set to true, use user-defined USSN kicks (as a fixed value) in lieu of the Mandel & Muller kick prescription for USSNe +// - Replaced --scale-CHE-mass-loss-with-surface-helium-abundance with the more general --scale-mass-loss-with-surface-helium-abundance (applies to all MS stars, not just CHE stars) +// - Updated rotational velocity solver to use boost root finder +// 03.27.01 JR - October 27, 2025 - Defect repairs: +// - changed Options::OptionValue() to return correct value for option --USSN-kicks-override-mandel-muller +// - deprecated --scale-CHE-mass-loss-with-surface-helium-abundance in favour of --scale-mass-loss-with-surface-helium-abundance +// - removed option --scale-CHE-mass-loss-with-surface-helium-abundance from "BSEonly" vector in Options.h +// - changed "scale-CHE-mass-loss-with-surface-helium-abundance" to "--scale-mass-loss-with-surface-helium-abundance" in "RangeExcluded" vector in Options.h +// - version should have been "v03.27.00" instead of "v03.26.02" - change included new functionality. This version is "v03.27.01" to compensate. +// 03.27.02 AB - December 9, 2025 - Defect repairs: +// - Reverted a change from PR #1437: --scale-mass-loss-with-surface-helium-abundance is changed back to +// --scale-CHE-mass-loss-with-surface-helium-abundance and applies only to CHE stars +// - Corrected behaviour of MS stars that stopped ageing after mass transfer when mass loss is disabled (issue #1444) +// - Corrected luminosity evolution for CH stars (issue #1443) +// 03.27.03 IM - December 16, 2025 - Defect repair: +// - Fix issue #1446: Theta and phi variables are flipped when assigning kicks, potentially giving unintended kick distributions +// +// +// Version string format is MM.mm.rr, where +// +// MM is the MAJOR release number: this should be incremented whenever major new functionality is introduced +// mm is the MINOR release number: this should be incremented whenever minor new functionality (e.g. small enhancemet) is introduced +// rr is the fix RELEASE number: this should be incremented whenever a defect repair is made +// +// if MM is incremented, set mm and rr to 00, even if defect repairs and minor enhancements were also made +// if mm is incremented, set rr to 00, even if defect repairs were also made + +const std::string VERSION_STRING = "03.27.03"; -const std::string VERSION_STRING = "03.17.00"; # endif // __changelog_h__ diff --git a/src/constants.h b/src/constants.h index 3a0367da2..090e0f764 100755 --- a/src/constants.h +++ b/src/constants.h @@ -10,6 +10,26 @@ #include #include + +// common type definitions +// easiest way of making them available globally is to put them here +typedef std::vector STR_VECTOR; +typedef std::vector DBL_VECTOR; +typedef std::vector INT_VECTOR; +typedef std::vector BOOL_VECTOR; +typedef std::tuple DBL_DBL; +typedef std::tuple DBL_DBL_DBL; +typedef std::tuple DBL_DBL_DBL_DBL; +typedef std::tuple STR_STR; +typedef std::tuple STR_STR_STR; +typedef std::tuple STR_STR_STR_STR; + +typedef std::vector> GE_QCRIT_RADII_QCRIT_VECTOR; +typedef std::tuple GE_QCRIT_TABLE; +typedef std::vector> GE_QCRIT_RADII_QCRIT_VECTOR_HE; +typedef std::tuple GE_QCRIT_TABLE_HE; + + // the defaults size of the boost list that handles variant types is 20 - so only 20 variant types are allowed // we've exceeded that number - we're at 21 currently - so the size of the boost list needs to be increased // we have to set the size of the list before we include the boost headers - otherwise boost redefines it @@ -62,23 +82,6 @@ typedef unsigned long int OBJECT_ID; extern OBJECT_ID globalObjectId; // used to uniquely identify objects - used primarily for error printing -// common type definitions -// easiest way of making them available globally is to put them here -typedef std::vector STR_VECTOR; -typedef std::vector DBL_VECTOR; -typedef std::vector INT_VECTOR; -typedef std::vector BOOL_VECTOR; -typedef std::tuple DBL_DBL; -typedef std::tuple DBL_DBL_DBL; -typedef std::tuple DBL_DBL_DBL_DBL; -typedef std::tuple STR_STR; -typedef std::tuple STR_STR_STR; -typedef std::tuple STR_STR_STR_STR; -typedef std::vector> GE_QCRIT_RADII_QCRIT_VECTOR; -typedef std::tuple GE_QCRIT_TABLE; -typedef std::vector> GE_QCRIT_RADII_QCRIT_VECTOR_HE; -typedef std::tuple GE_QCRIT_TABLE_HE; - #include "typedefs.h" @@ -184,10 +187,13 @@ constexpr double G_km_Msol_s = G * 1.0E-9 / KG_TO_MSO constexpr double G_SOLAR_YEAR = 3.14E7; // Gravitational constant in Lsol Rsol yr Msol^-2 for calculating photon tiring limit constexpr double RSOL = 6.957E8; // Solar Radius (in m) -constexpr double ZSOL = 0.02; // Solar Metallicity used in scalings -constexpr double LOG10_ZSOL = -1.698970004336019; // log10(ZSOL) - for performance -constexpr double ZSOL_ASPLUND = 0.0142; // Solar Metallicity (Asplund+ 2010) used in initial condition -constexpr double YSOL = 0.2485; // Asplund+ 2009 +constexpr double ZSOL_HURLEY = 0.02; // Solar Metallicity used in scalings +constexpr double LOG10_ZSOL_HURLEY = -1.698970004336019; // log10(ZSOL_HURLEY) - for performance +constexpr double ZSOL_ANDERS = 0.019; // Solar Metallicity (Anders+ 1989) used in winds +constexpr double LOG10_ZSOL_ANDERS = -1.721246399047171; // log10(ZSOL_ANDERS) - for performance +constexpr double ZSOL_ASPLUND = 0.0142; // Solar Metallicity (Asplund+ 2009) used in initial condition and winds +constexpr double LOG10_ZSOL_ASPLUND = -1.847711655616944; // log10(ZSOL_ASPLUND) - for performance +constexpr double YSOL_ASPLUND = 0.2485; // Asplund+ 2009 constexpr double TSOL = 5778.0; // Solar Temperature in kelvin constexpr double LSOL = 3.844E33; // Solar Luminosity in erg/s constexpr double LSOLW = 3.844E26; // Solar luminosity (in W) @@ -242,6 +248,7 @@ constexpr double MINIMUM_BLUE_LOOP_FRACTION = 1.0E-10; constexpr double TIMESTEP_QUANTUM = 1.0E-12; // Timestep quantum in Myr (=31.5576 seconds, given DAYS_IN_QUAD) constexpr double ABSOLUTE_MINIMUM_TIMESTEP = 3.0 * TIMESTEP_QUANTUM; // In Myr (=94.6728 seconds, given TIMESTEP QUANTUM) constexpr double NUCLEAR_MINIMUM_TIMESTEP = 1.0E6 * TIMESTEP_QUANTUM; // Minimum time step for nuclear evolution in My (= 1 year = 31557600 seconds, given TIMESTEP_QUANTUM) +constexpr double MAXIMUM_TIMESTEP_MULTIPLIER = 1.0E3; // Maximum timestep multiplier constexpr unsigned int ABSOLUTE_MAXIMUM_TIMESTEPS = 1000000; // Absolute maximum number of timesteps @@ -252,8 +259,6 @@ constexpr double MAXIMUM_MASS_LOSS_FRACTION = 0.001; constexpr double MAXIMUM_RADIAL_CHANGE = 0.1; // Maximum allowable radial change - 10% (of radius) expressed as a fraction constexpr double MAXIMUM_WIND_MASS_LOSS_RATE = 0.1; // Maximum wind mass loss rates (in solar masses per year) to avoid convergence issues -constexpr double MINIMUM_MASS_SECONDARY = 4.0; // Minimum mass of secondary to evolve - constexpr double LAMBDA_NANJING_ZLIMIT = 0.0105; // Metallicity cutoff for Nanjing lambda calculations constexpr double LAMBDA_NANJING_POPI_Z = 0.02; // Population I metallicity in Xu & Li (2010) constexpr double LAMBDA_NANJING_POPII_Z = 0.001; // Population II metallicity in Xu & Li (2010) @@ -274,6 +279,10 @@ constexpr int ADAPTIVE_RLOF_MAX_TRIES = 30; constexpr int ADAPTIVE_RLOF_MAX_ITERATIONS = 50; // Maximum number of root finder iterations in BaseBinaryStar::MassLossToFitInsideRocheLobe() constexpr double ADAPTIVE_RLOF_SEARCH_FACTOR_FRAC = 1.0; // Search size factor (fractional part) in BaseBinaryStar::MassLossToFitInsideRocheLobe() (added to 1.0) +constexpr int ADAPTIVE_RV_MAX_TRIES = 30; // Maximum number of tries in BaseStar::CalculateOStarRotationalVelocity_Static() +constexpr int ADAPTIVE_RV_MAX_ITERATIONS = 50; // Maximum number of root finder iterations in BaseStar::CalculateOStarRotationalVelocity_Static() +constexpr double ADAPTIVE_RV_SEARCH_FACTOR_FRAC = 1.0; // Search size factor (fractional part) in BaseStar::CalculateOStarRotationalVelocity_Static() (added to 1.0) + constexpr int ADAPTIVE_MASS0_MAX_TRIES = 30; // Maximum number of tries in HG::Mass0ToMatchDesiredCoreMass() constexpr int ADAPTIVE_MASS0_MAX_ITERATIONS = 50; // Maximum number of iterations in HG::Mass0ToMatchDesiredCoreMass() constexpr double ADAPTIVE_MASS0_SEARCH_FACTOR_FRAC = 1.0; // Search size factor (fractional part) in HG::Mass0ToMatchDesiredCoreMass() (added to 1.0) @@ -289,8 +298,8 @@ constexpr int TIDES_OMEGA_MAX_TRIES = 30; constexpr int TIDES_OMEGA_MAX_ITERATIONS = 50; // Maximum number of root finder iterations in BaseBinaryStar::OmegaAfterCircularisation() constexpr double TIDES_OMEGA_SEARCH_FACTOR_FRAC = 1.0; // Search size factor (fractional part) in BaseBinaryStar::OmegaAfterCircularisation() (added to 1.0) constexpr double TIDES_MINIMUM_FRACTIONAL_EXTENT = 1.0E-4; // Minimum fractional radius or mass of the stellar core or envelope, above which a given tidal dissipation mechanism is considered applicable -constexpr double TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC = 0.01; // Maximum allowed change in orbital and spin properties due to KAPIL2024 tides in a single timestep - 1% expressed as a fraction -constexpr double TIDES_MINIMUM_FRACTIONAL_NUCLEAR_TIME = 0.001; // Minimum allowed timestep from tidal processes, as a fraction of the nuclear minimum time scale +constexpr double TIDES_MAXIMUM_ORBITAL_CHANGE_FRAC = 0.01; // Maximum allowed change in orbital and spin properties due to KAPIL2025 tides in a single timestep - 1% expressed as a fraction +constexpr double TIDES_MINIMUM_FRACTIONAL_NUCLEAR_TIME = 0.001; // Minimum allowed timestep from tidal processes, as a fraction of the nuclear minimum time scale constexpr double FARMER_PPISN_UPP_LIM_LIN_REGIME = 38.0; // Maximum CO core mass to result in the linear remnant mass regime of the FARMER PPISN prescription constexpr double FARMER_PPISN_UPP_LIM_QUAD_REGIME = 60.0; // Maximum CO core mass to result in the quadratic remnant mass regime of the FARMER PPISN prescription @@ -302,6 +311,10 @@ constexpr double Q_CNO = 9.9073E4; // Initial mass of stars above which (including the limit) we allow convective core mass calculations from Shikauchi et al. (2024) and rejuvenation calculations // Note that this value should always be > 0.7 Msol constexpr double BRCEK_LOWER_MASS_LIMIT = 1.5; +// Maximum core mass to total mass ratio on the main sequence (when BRCEK core mass prescription is used) +// Sets upper limit on the main-sequence convective core mass to prevent the star from becoming fully convective +// Detailed models from MESA suggest that the convective core mass never exceeds ~90% of the total mass +constexpr double BRCEK_CORE_MASS_TO_MASS_RATIO_LIMIT = 0.9; // logging constants @@ -311,9 +324,9 @@ const std::string DEFAULT_HDF5_FILE_NAME = "COMPAS_Output"; const std::string DETAILED_OUTPUT_DIRECTORY_NAME = "Detailed_Output"; // Name for detailed output directory within output container const std::string RUN_DETAILS_FILE_NAME = "Run_Details"; // Name for run details output file within output container -constexpr int HDF5_DEFAULT_CHUNK_SIZE = 100000; // default HDF5 chunk size (number of dataset entries) -constexpr int HDF5_DEFAULT_IO_BUFFER_SIZE = 1; // number of HDF5 chunks to buffer for IO (per open dataset) -constexpr int HDF5_MINIMUM_CHUNK_SIZE = 1000; // minimum HDF5 chunk size (number of dataset entries) +constexpr int HDF5_DEFAULT_CHUNK_SIZE = 100000; // Default HDF5 chunk size (number of dataset entries) +constexpr int HDF5_DEFAULT_IO_BUFFER_SIZE = 1; // Number of HDF5 chunks to buffer for IO (per open dataset) +constexpr int HDF5_MINIMUM_CHUNK_SIZE = 1000; // Minimum HDF5 chunk size (number of dataset entries) // option constraints // Use these constant to specify constraints that should be applied to program option values @@ -381,7 +394,15 @@ constexpr double MULLERMANDEL_SIGMABH = 0.5; constexpr double MULLERMANDEL_MINNS = 1.13; constexpr double MULLERMANDEL_KICKNS = 520.0; // As calibrated by Kapil+ 2023 constexpr double MULLERMANDEL_KICKBH = 200.0; -constexpr double MULLERMANDEL_SIGMAKICK = 0.3; +constexpr double MULLERMANDEL_SIGMAKICKBH = 0.3; +constexpr double MULLERMANDEL_SIGMAKICKNS = 0.3; + +// Constants for Disberg & Mandel (2025) SN kick prescription +constexpr double DISBERG_MANDEL_MU = 5.60; +constexpr double DISBERG_MANDEL_SIGMA = 0.68; +constexpr double DISBERG_MANDEL_MAX_KICK = 1000.0; + +constexpr double HOBBS_CORRECTED_SIGMA = 217.0; // Best fit Maxwellian sigma for 48 younger than 10 Myr pulsars with proper motions from Disberg & Mandel (2025) sample; corrects Hobbs+ 2005 missing Jacobian // Constants for the Maltsev+ 2024 SN remnant mass prescription constexpr double MALTSEV2024_MMIN = 5.62; @@ -394,7 +415,7 @@ constexpr double MALTSEV2024_M2S = 7.2; constexpr double MALTSEV2024_M2C = 7.1; constexpr double MALTSEV2024_M2B = 8.3; constexpr double MALTSEV2024_M2A = 8.4; -constexpr double MALTSEV2024_M3S = 12.9; +constexpr double MALTSEV2024_M3S = 13.0; constexpr double MALTSEV2024_M3C = 13.2; constexpr double MALTSEV2024_M3B = 15.2; constexpr double MALTSEV2024_M3A = 15.4; @@ -419,20 +440,55 @@ constexpr double HEWD_HE_MDOT_CRIT = 2.0E-8; constexpr double HEWD_MINIMUM_MASS_IGNITION = 0.35; // Minimum mass for HeMS burning constexpr double MASS_DOUBLE_DETONATION_CO = 0.9; // Minimum mass for detonation which would yield something similar to SN Ia. Ruiter+ 2014. constexpr double Q_HYDROGEN_BURNING = 6.4E18 * MSOL_TO_G / (SECONDS_IN_YEAR * LSOL); // 6.4E18 is the energy yield of H burning in erg/g as given in Nomoto+ 2007 (2007ApJ...663.1269N) +constexpr double L0_RITTER_HIGH_Z = 1995262.3; // Luminosity constant which depends on metallicity in Ritter 1999, eq 10 +constexpr double L0_RITTER_LOW_Z = 31622.8; constexpr double WD_HE_SHELL_MCRIT_DETONATION = 0.05; // Minimum shell mass of He for detonation. Should be composed of helium (so, exclude burnt material), but not implemented yet. Ruiter+ 2014. -constexpr double WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_0 = -6.84; -constexpr double WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_1 = 1.349; -constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_0 = -8.115; -constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_1 = 2.29; -constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_0 = -8.313; -constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_1 = 1.018; -constexpr double WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_0 = -8.33017155; -constexpr double WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_1 = 2.88247131; -constexpr double WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_2 = -0.98023471; -constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_0 = -9.21757267; -constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_1 = 3.57319872; -constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_2 = -1.2137735; - +constexpr double WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_0 = -6.84; // Constant from Piersanti et al. 2014 see Table A1 +constexpr double WD_LOG_MT_LIMIT_PIERSANTI_RG_SS_1 = 1.349; // Constant from Piersanti et al. 2014 see Table A1 +constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_0 = -8.115; // Constant from Piersanti et al. 2014 see Table A1 +constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SS_MF_1 = 2.29; // Constant from Piersanti et al. 2014 see Table A1 +constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_0 = -8.313; // Constant from Piersanti et al. 2014 see Table A1 +constexpr double WD_LOG_MT_LIMIT_PIERSANTI_SF_Dt_1 = 1.018; // Constant from Piersanti et al. 2014 see Table A1 +constexpr double WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_0 = -8.3302; // Quadratic fit to results from Nomoto et al. 2007 Table 5 +constexpr double WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_1 = 2.8825; // Quadratic fit to results from Nomoto et al. 2007 Table 5 +constexpr double WD_LOG_MT_LIMIT_NOMOTO_REDGIANT_2 = -0.9802; // Quadratic fit to results from Nomoto et al. 2007 Table 5 +constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_0 = -9.2176; // Quadratic fit to results from Nomoto et al. 2007 Table 5 +constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_1 = 3.5732; // Quadratic fit to results from Nomoto et al. 2007 Table 5 +constexpr double WD_LOG_MT_LIMIT_NOMOTO_STABLE_2 = -1.2138; // Quadratic fit to results from Nomoto et al. 2007 Table 5 +constexpr double WD_MP = 5.7E-4; // White dwarf mass parameter determining where mass-radius relation changes for low-mass white dwarfs +constexpr double WD_BELCZYNSKI_SN_CONSTANT = 1.34; // Constant from Eq 62, Belczynski+ 2008 +constexpr double WD_BELCZYNSKI_SN_LINEAR = 4.0E8; // Linear term factor from Eq 62, Belczynski+ 2008 +constexpr double WD_BELCZYNSKI_IMMEDIATE_FLASH = 1.64E-6; // Accretion limit from eq 61, Belczynski+ 2008. +constexpr double WD_BELCZYNSKI_MINIMUM_HE_CONSTANT = 0.13; // Constant from Eq 61, Belczynski+ 2008 +constexpr double WD_BELCZYNSKI_MINIMUM_HE_LINEAR = 7.8E-4; // Linear term factor from Eq 61, Belczynski+ 2008 +constexpr double WD_PIERSANTI_M060_G0 = 6.0E-3; // This and the following PIERSANTI constants follow table A3 in Piersanti+2014 +constexpr double WD_PIERSANTI_M060_G1 = 5.1E-2; +constexpr double WD_PIERSANTI_M060_G2 = 8.3E-3; +constexpr double WD_PIERSANTI_M060_G3 = 3.317E-4; +constexpr double WD_PIERSANTI_M070_G0 = 3.5E-2; +constexpr double WD_PIERSANTI_M070_G1 = 7.5E-2; +constexpr double WD_PIERSANTI_M070_G2 = 1.8E-3; +constexpr double WD_PIERSANTI_M070_G3 = 3.266E-5; +constexpr double WD_PIERSANTI_M081_G0 = 9.3E-2; +constexpr double WD_PIERSANTI_M081_G1 = 1.8E-2; +constexpr double WD_PIERSANTI_M081_G2 = 1.6E-3; +constexpr double WD_PIERSANTI_M081_G3 = 4.111E-5; +constexpr double WD_PIERSANTI_M092_G0 = 7.59E-2; +constexpr double WD_PIERSANTI_M092_G1 = 1.54E-2; +constexpr double WD_PIERSANTI_M092_G2 = 4.0E-4; +constexpr double WD_PIERSANTI_M092_G3 = 5.905E-6; +constexpr double WD_PIERSANTI_M102_G0 = 3.23E-1; +constexpr double WD_PIERSANTI_M102_G1 = 4.1E-2; +constexpr double WD_PIERSANTI_M102_G2 = 7.0E-4; +constexpr double WD_PIERSANTI_M102_G3 = 4.733E-6; + +// Critical mass ratio constants for CalculateCriticalMassRatioHurleyHjellmingWebbink(). +// Based on Hurley+ 2002 section 2.6.1 and BSE code (inverse of the quoted values). +constexpr double HURLEY_HJELLMING_WEBBINK_QCRIT_MS_LTE_07 = 1.44; +constexpr double HURLEY_HJELLMING_WEBBINK_QCRIT_MS_GT_07 = 0.33; +constexpr double HURLEY_HJELLMING_WEBBINK_QCRIT_HG = 0.25; +constexpr double HURLEY_HJELLMING_WEBBINK_QCRIT_HE_GIANT = 1.28; +constexpr double HURLEY_HJELLMING_WEBBINK_QCRIT_WD = 1.59; // coefficients for the calculation of initial angular frequency for Chemically Homogeneous Evolution // Mandel from Butler 2018 @@ -3748,6 +3804,8 @@ const std::vector>> LOVERIDGE_COE // Coefficients for determining Main Sequence core mass // from Shikauchi et al. (2024), https://arxiv.org/abs/2409.00460 +// Section A.4 +const DBL_VECTOR SHIKAUCHI_DELTA_COEFFICIENTS = {0.54491412, -0.00900365, 0.08936248}; // Table 2 const std::vector SHIKAUCHI_ALPHA_COEFFICIENTS = { {0.45, -0.0557105, -0.86589929}, // 0.1*Z_Sun @@ -3762,9 +3820,13 @@ const std::vector SHIKAUCHI_FMIX_COEFFICIENTS = { }; // Table 4 const std::vector SHIKAUCHI_L_COEFFICIENTS = { - {3.2555795, 1.84666823, -0.79986388, -0.75728099, -0.38831172, 0.08223542, 0.49543834, 0.31314176, -0.36705796, 1.72200581}, // 0.1*Z_Sun - {3.35622529, 1.96904931, -0.88894808, -0.81112488, -0.47925922, 0.09056925, 0.53094768, 0.33971972, -0.35581284, 1.65390003}, // 1/3*Z_Sun - {3.27883249, 1.79370338, -0.71413866, -0.77019351, -0.3898752, 0.07499563, 0.5920458, 0.33846556, -0.49649838, 1.71263853} // Solar metallicity Z_Sun + {3.38627891, 1.13599187, -0.97389238, -0.87675442, 1.65386007, 0.07661174, -1.78737297, 0.622451, -0.47511355, 0.02483567, 0.94243277, -0.06798225, 0.11086108, -0.14859538, 1.78029915}, // 0.1*Z_Sun + {3.45464814, 0.94880846, -1.11409154, -0.86672079, 2.38986855, 0.04448855, -2.74913945, 0.60905625, -0.27648361, 0.03514139, 1.37569819, -0.19184532, 0.12816567, -0.14392935, 1.76390159}, // 1/3*Z_Sun + {3.80166901, 0.37407948, -1.29904749, -1.34541622, 3.70934166, 0.28320469, -3.92327169, 0.92444477, -0.40146717, -0.00821364, 1.80297947, -0.15776603, 0.09205681, -0.21913557, 1.78496679} // Solar metallicity Z_Sun }; +// Coefficients used to determine the initial convective core mass of MS star after full mixing (due to a merger or CHE) +// from Brcek et al. (2025) +const DBL_VECTOR BRCEK_FMIX_COEFFICIENTS = {0.898171018326982, -0.592244880828559, 55.7885260968562, 0.359078394562545, 1.87717633667786}; + #endif // __constants_h__ diff --git a/src/main.cpp b/src/main.cpp index 08c1cee95..fab59dbc9 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -400,7 +400,7 @@ std::tuple EvolveSingleStars() { : new Star(randomSeed, initialMass, metallicity, kickParameters); // no - let it be calculated thisStarStatus = EVOLUTION_STATUS::STARTED; - + thisStarStatus = star->Evolve(index); // evolve the star // announce the result diff --git a/src/typedefs.h b/src/typedefs.h index 6ec3a3656..04a2f9690 100755 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -148,7 +148,9 @@ enum class STELLAR_TYPE: int { // Hurley CHEMICALLY_HOMOGENEOUS, // 16 : this is here to preserve the Hurley type numbers, but note that Hurley type number progression doesn't necessarily indicate class inheritance STAR, // 17 : star is created this way, then switches as required (down here so stellar types consistent with Hurley et al. 2000) BINARY_STAR, // 18 : here mainly for diagnostics - NONE // 19 : here mainly for diagnostics + NONE, // 19 : here mainly for diagnostics + + COUNT // Sentinel for entry count }; const COMPASUnorderedMap STELLAR_TYPE_LABEL = { { STELLAR_TYPE::MS_LTE_07, "Main_Sequence_<=_0.7" }, @@ -386,9 +388,9 @@ const COMPASUnorderedMap CHE_MODE_LABEL = { }; // main sequence core mass prescription -enum class CORE_MASS_PRESCRIPTION: int { ZERO, MANDEL, BRCEK }; +enum class CORE_MASS_PRESCRIPTION: int { HURLEY, MANDEL, BRCEK }; const COMPASUnorderedMap CORE_MASS_PRESCRIPTION_LABEL = { - { CORE_MASS_PRESCRIPTION::ZERO, "ZERO" }, + { CORE_MASS_PRESCRIPTION::HURLEY, "HURLEY" }, { CORE_MASS_PRESCRIPTION::MANDEL, "MANDEL" }, { CORE_MASS_PRESCRIPTION::BRCEK, "BRCEK" } }; @@ -520,7 +522,6 @@ enum class GBP: int { McBGB, // Core mass at BGB (Base of Giant Branch) McBAGB, // Core mass at BAGB (Base of Asymptotic Giant Branch). Hurley et al. 2000, eq 66 (also see eq 75 and discussion) McDU, // Core mass at second dredge up. Hurley et al. 2000, eq 69 - McSN, // Core mass at which the Asymptotic Giant Branch phase is terminated in a SN/loss of envelope COUNT // Sentinel for entry count }; @@ -535,7 +536,7 @@ const COMPASUnorderedMap INITIAL_MASS_FUNCTI }; // kick magnitude distributions -enum class KICK_MAGNITUDE_DISTRIBUTION: int { ZERO, FIXED, FLAT, MAXWELLIAN, BRAYELDRIDGE, MULLER2016, MULLER2016MAXWELLIAN, MULLERMANDEL}; +enum class KICK_MAGNITUDE_DISTRIBUTION: int { ZERO, FIXED, FLAT, MAXWELLIAN, BRAYELDRIDGE, MULLER2016, MULLER2016MAXWELLIAN, MULLERMANDEL, LOGNORMAL}; const COMPASUnorderedMap KICK_MAGNITUDE_DISTRIBUTION_LABEL = { { KICK_MAGNITUDE_DISTRIBUTION::ZERO, "ZERO" }, { KICK_MAGNITUDE_DISTRIBUTION::FIXED, "FIXED" }, @@ -544,7 +545,8 @@ const COMPASUnorderedMap KICK_MAGNITUD { KICK_MAGNITUDE_DISTRIBUTION::BRAYELDRIDGE, "BRAYELDRIDGE" }, { KICK_MAGNITUDE_DISTRIBUTION::MULLER2016, "MULLER2016" }, { KICK_MAGNITUDE_DISTRIBUTION::MULLER2016MAXWELLIAN, "MULLER2016MAXWELLIAN" }, - { KICK_MAGNITUDE_DISTRIBUTION::MULLERMANDEL, "MULLERMANDEL" } + { KICK_MAGNITUDE_DISTRIBUTION::MULLERMANDEL, "MULLERMANDEL" }, + { KICK_MAGNITUDE_DISTRIBUTION::LOGNORMAL, "LOGNORMAL" } }; // kick direction distributions @@ -596,12 +598,12 @@ enum class MASS_CUTOFF: int { }; // mass loss prescriptions -enum class MASS_LOSS_PRESCRIPTION: int { ZERO, HURLEY, BELCZYNSKI2010, MERRITT2024 }; +enum class MASS_LOSS_PRESCRIPTION: int { ZERO, HURLEY, BELCZYNSKI2010, MERRITT2025 }; const COMPASUnorderedMap MASS_LOSS_PRESCRIPTION_LABEL = { { MASS_LOSS_PRESCRIPTION::ZERO, "ZERO" }, { MASS_LOSS_PRESCRIPTION::HURLEY, "HURLEY" }, { MASS_LOSS_PRESCRIPTION::BELCZYNSKI2010, "BELCZYNSKI2010" }, - { MASS_LOSS_PRESCRIPTION::MERRITT2024, "MERRITT2024" } + { MASS_LOSS_PRESCRIPTION::MERRITT2025, "MERRITT2025" } }; // symbolic names for mass loss rate type @@ -625,12 +627,12 @@ const COMPASUnorderedMap MASS_RATIO_DISTRI }; // mass transfer timescale types -enum class MASS_TRANSFER_TIMESCALE: int { NONE, NUCLEAR, THERMAL, CE }; -const COMPASUnorderedMap MASS_TRANSFER_TIMESCALE_LABEL = { - { MASS_TRANSFER_TIMESCALE::NONE, "NONE" }, - { MASS_TRANSFER_TIMESCALE::NUCLEAR, "NUCLEAR" }, - { MASS_TRANSFER_TIMESCALE::THERMAL, "THERMAL" }, - { MASS_TRANSFER_TIMESCALE::CE, "CE" } +enum class MT_TIMESCALE: int { NONE, NUCLEAR, THERMAL, CE }; +const COMPASUnorderedMap MT_TIMESCALE_LABEL = { + { MT_TIMESCALE::NONE, "NONE" }, + { MT_TIMESCALE::NUCLEAR, "NUCLEAR" }, + { MT_TIMESCALE::THERMAL, "THERMAL" }, + { MT_TIMESCALE::CE, "CE" } }; // metallicity distributions @@ -641,19 +643,21 @@ const COMPASUnorderedMap METALLICITY_DIST }; // mass transfer accretion efficiency prescriptions -enum class MT_ACCRETION_EFFICIENCY_PRESCRIPTION: int { THERMALLY_LIMITED, FIXED_FRACTION }; +enum class MT_ACCRETION_EFFICIENCY_PRESCRIPTION: int { THERMALLY_LIMITED, FIXED_FRACTION, HAMSTARS }; const COMPASUnorderedMap MT_ACCRETION_EFFICIENCY_PRESCRIPTION_LABEL = { { MT_ACCRETION_EFFICIENCY_PRESCRIPTION::THERMALLY_LIMITED, "THERMAL" }, - { MT_ACCRETION_EFFICIENCY_PRESCRIPTION::FIXED_FRACTION, "FIXED" } + { MT_ACCRETION_EFFICIENCY_PRESCRIPTION::FIXED_FRACTION, "FIXED" }, + { MT_ACCRETION_EFFICIENCY_PRESCRIPTION::HAMSTARS, "HAMSTARS"} }; // mass transfer angular momentum loss prescriptions -enum class MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION: int { JEANS, ISOTROPIC_RE_EMISSION, CIRCUMBINARY_RING, MACLEOD_LINEAR, ARBITRARY }; +enum class MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION: int { JEANS, ISOTROPIC_RE_EMISSION, CIRCUMBINARY_RING, MACLEOD_LINEAR, KLENCKI_LINEAR, ARBITRARY }; const COMPASUnorderedMap MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION_LABEL = { { MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::JEANS, "JEANS" }, { MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::ISOTROPIC_RE_EMISSION, "ISOTROPIC" }, { MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::CIRCUMBINARY_RING, "CIRCUMBINARY" }, { MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::MACLEOD_LINEAR, "MACLEOD_LINEAR" }, + { MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::KLENCKI_LINEAR, "KLENCKI_LINEAR" }, { MT_ANGULAR_MOMENTUM_LOSS_PRESCRIPTION::ARBITRARY, "ARBITRARY" } }; @@ -814,6 +818,14 @@ const COMPASUnorderedMap REMNANT_MASS_PR { REMNANT_MASS_PRESCRIPTION::MALTSEV2024, "MALTSEV2024" } }; +// maltsev remnant mass prescription variant +enum class MALTSEV_MODE: int { OPTIMISTIC, BALANCED, PESSIMISTIC }; +const COMPASUnorderedMap MALTSEV_MODE_LABEL = { + { MALTSEV_MODE::OPTIMISTIC, "OPTIMISTIC" }, + { MALTSEV_MODE::BALANCED, "BALANCED" }, + { MALTSEV_MODE::PESSIMISTIC, "PESSIMISTIC"}, +}; + // response of star to spin-up beyond the Keplerian frequency enum class RESPONSE_TO_SPIN_UP: int { TRANSFER_TO_ORBIT, KEPLERIAN_LIMIT, NO_LIMIT }; const COMPASUnorderedMap RESPONSE_TO_SPIN_UP_LABEL = { @@ -925,11 +937,11 @@ const COMPASUnorderedMap STELLAR_POPULATION_LAB }; // tides prescriptions -enum class TIDES_PRESCRIPTION: int { NONE, PERFECT, KAPIL2024 }; +enum class TIDES_PRESCRIPTION: int { NONE, PERFECT, KAPIL2025 }; const COMPASUnorderedMap TIDES_PRESCRIPTION_LABEL = { { TIDES_PRESCRIPTION::NONE, "NONE" }, { TIDES_PRESCRIPTION::PERFECT, "PERFECT" }, - { TIDES_PRESCRIPTION::KAPIL2024, "KAPIL2024" } + { TIDES_PRESCRIPTION::KAPIL2025, "KAPIL2025" } }; // symbolic names for timescales @@ -1017,7 +1029,6 @@ const COMPASUnorderedMap ZETA_PRESCRIPTION_LABEL }; - // boost variant definition for allowed data types // used for variable specification to define logfile records typedef boost::variant< @@ -1033,28 +1044,26 @@ typedef boost::variant< float, double, long double, + DBL_VECTOR, std::string, - std::vector, + STR_VECTOR, ERROR, STELLAR_TYPE, MT_CASE, MT_TRACKING, - MASS_TRANSFER_TIMESCALE, + MT_TIMESCALE, SN_EVENT, SN_STATE, EVOLUTION_STATUS > COMPAS_VARIABLE; - - // common type definitions typedef std::initializer_list SN_EVENT_LIST; typedef std::vector ST_VECTOR; typedef std::vector COMPAS_VARIABLE_VECTOR; - // Option details typedef struct OptionDetails { std::string optionStr; // name string @@ -1203,6 +1212,11 @@ typedef struct RLOFProperties { double radius1; double radius2; + double temperature1; + double temperature2; + double luminosity1; + double luminosity2; + double starToRocheLobeRadiusRatio1; double starToRocheLobeRadiusRatio2; @@ -1221,7 +1235,7 @@ typedef struct RLOFProperties { double massLossRateFromDonor; double accretionEfficiency; - MASS_TRANSFER_TIMESCALE massTransferTimescale; + MT_TIMESCALE massTransferTimescale; } RLOFPropertiesT; @@ -1253,6 +1267,7 @@ typedef struct BinaryCEESavedValues { double rocheLobe1to2; double rocheLobe2to1; double semiMajorAxis; + double semiMajorAxisAfterStage1; } BinaryCEESavedValuesT; // JR: add descriptive comments @@ -1292,7 +1307,6 @@ typedef struct StellarCEDetails { // Common Envelope d double lambda; double convectiveEnvelopeMass; // for two-stage CE formalism double radiativeIntershellMass; // for two-stage CE formalism - double convectiveEnvelopeBindingEnergy; // for two-stage CE formalism } StellarCEDetailsT; // was CommonEnvelopeDetailsT; diff --git a/src/utils.cpp b/src/utils.cpp index 228ea8951..536ec934e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1469,7 +1469,7 @@ namespace utils { // Construct the splash string std::string splashString = "\nCOMPAS v" + - VERSION_STRING + + VERSION_STRING + " (gsl v" + GetGSLVersion() + ", boost v" + GetBOOSTVersion() + ", HDF5 v" + GetHDF5Version() + ")" + "\nCompact Object Mergers: Population Astrophysics and Statistics" "\nby Team COMPAS (http://compas.science/index.html)" "\nA binary star simulator\n" @@ -1820,4 +1820,51 @@ namespace utils { } } } + + + /* + * Returns gsl version string + * + * + * std::string GetGSLVersion() + * + * @return String containing GSL version in format MM.mm.rr + * Will be "Not available" if not able to retrieve the actual value + */ + std::string GetGSLVersion() { + + std::string versionStr = "Not available"; // default return value + + char buffer[128]; // command return buffer + std::unique_ptr pipe(popen("gsl-config --version", "r"), &pclose); // open pipe for command + if (pipe) { // ok? + versionStr = ""; // yes + while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) versionStr += buffer; // copy buffer + if (!versionStr.empty() && versionStr[versionStr.length() - 1] == '\n') versionStr.pop_back(); // remove trailing newline if necessary + } + + return versionStr; + } + + + /* + * Returns HDF5 library version string + * + * + * std::string GetHDF5Version() + * + * @return String containing HDF5 library version in format MM.mm.rr + * Will be "Not available" if not able to retrieve the actual value + */ + std::string GetHDF5Version() { + + std::string versionStr = "Not available"; // default return value + + unsigned majorNum, minorNum, releaseNum; + herr_t status = H5get_libversion(&majorNum, &minorNum, &releaseNum); // retrieve HDF5 library version + if (status >= 0) // ok? + versionStr = std::to_string(majorNum) + "." + std::to_string(minorNum) + "." + std::to_string(releaseNum); // yes - set version string + + return versionStr; + } } diff --git a/src/utils.h b/src/utils.h index 410aa83eb..a5e3d1e5a 100755 --- a/src/utils.h +++ b/src/utils.h @@ -1,7 +1,11 @@ #ifndef __utils_h__ #define __utils_h__ +#include + +#include #include +#include "hdf5.h" #include "constants.h" #include "typedefs.h" @@ -83,6 +87,10 @@ namespace utils { return std::make_tuple(false, p_Default); } + inline std::string GetBOOSTVersion() { return std::to_string(BOOST_VERSION / 100000) + "." + std::to_string(BOOST_VERSION / 100 % 1000) + "." + std::to_string(BOOST_VERSION % 100); } + std::string GetGSLVersion(); + std::string GetHDF5Version(); + double intPow(const double p_Base, const int p_Exponent); double InverseSampleFromPowerLaw(const double p_Power, const double p_Xmax, const double p_Xmin); diff --git a/src/yaml.h b/src/yaml.h index 4c957363d..e64190982 100644 --- a/src/yaml.h +++ b/src/yaml.h @@ -91,7 +91,6 @@ namespace yaml { "", " ### STELLAR PROPERTIES", " --check-photon-tiring-limit", - " --use-mass-loss", " --enable-rotationally-enhanced-mass-loss", " --enhance-CHE-lifetimes-luminosities", " --expel-convective-envelope-above-luminosity-threshold", @@ -124,6 +123,7 @@ namespace yaml { " --allow-non-stripped-ECSN", " --pair-instability-supernovae", " --pulsational-pair-instability", + " --USSN-kicks-override-mandel-muller", "", " ### PULSAR PARAMETERS", " --evolve-pulsars", @@ -142,6 +142,7 @@ namespace yaml { " --logfile-pulsar-evolution-record-types", " --logfile-rlof-parameters-record-types", " --logfile-supernovae-record-types", + " --logfile-system-snapshot-log-record-types", " --logfile-system-parameters-record-types", " --grid-lines-to-process", " --grid-start-line", @@ -153,14 +154,17 @@ namespace yaml { " --maximum-number-timestep-iterations", " --number-of-systems # number of systems per batch", " --radial-change-fraction # approximate desired fractional changes in stellar radius per timestep", + " --system-snapshot-age-thresholds # age thresholds for the system snapshot log", + " --system-snapshot-time-thresholds # time thresholds for the system snapshot log", " --timestep-multiplier # optional multiplier relative to default time step duration", + " --timestep-multipliers # optional phase-dependent multipliers relative to default time step duration", "", " ### STELLAR PROPERTIES", " --cool-wind-mass-loss-multiplier", " --initial-mass # initial mass for SSE", - " --initial-mass-min # use 5.0 for DCOs [Msol]", - " --initial-mass-max # stellar tracks extrapolated above 50 Msol (Hurley+2000) [Msol]", - " --initial-mass-power", + " --initial-mass-function-min # use 5.0 for DCOs [Msol]", + " --initial-mass-function-max # stellar tracks extrapolated above 50 Msol (Hurley+2000) [Msol]", + " --initial-mass-function-power", " --luminosity-to-mass-threshold", " --metallicity # metallicity for both SSE and BSE - Solar metallicity Asplund+2010", " --metallicity-min", @@ -183,7 +187,7 @@ namespace yaml { " --mass-ratio", " --mass-ratio-min", " --mass-ratio-max", - " --minimum-secondary-mass # Brown dwarf limit [Msol]", + " --minimum-sampled-secondary-mass # Brown dwarf limit [Msol]", " --orbital-period # orbital period for BSE", " --orbital-period-min # [days]", " --orbital-period-max # [days]", @@ -212,8 +216,8 @@ namespace yaml { " --critical-mass-ratio-white-dwarf-non-degenerate-accretor", " --mass-transfer-fa # Only if using mass-transfer-accretion-efficiency-prescription = 'FIXED'", " --mass-transfer-jloss # Only if using mass-transfer-angular-momentum-loss-prescription = 'FIXED'", - " --mass-transfer-jloss-macleod-linear-fraction-degen", - " --mass-transfer-jloss-macleod-linear-fraction-non-degen", + " --mass-transfer-jloss-linear-fraction-degen", + " --mass-transfer-jloss-linear-fraction-non-degen", " --mass-transfer-thermal-limit-C", " --zeta-adiabatic-arbitrary", " --zeta-main-sequence", @@ -228,6 +232,7 @@ namespace yaml { " --common-envelope-mass-accretion-max # For 'MACLEOD+2014' [Msol]", " --common-envelope-mass-accretion-min # For 'MACLEOD+2014' [Msol]", " --common-envelope-recombination-energy-density", + " --common-envelope-second-stage-beta", " --common-envelope-slope-kruckow", " --maximum-mass-donor-nandez-ivanova", "", @@ -259,7 +264,8 @@ namespace yaml { " --mcbur1", " --muller-mandel-kick-multiplier-BH # scaling prefactor for BH kicks when using the 'MULLERMANDEL' kick magnitude distribution", " --muller-mandel-kick-multiplier-NS # scaling prefactor for NS kicks when using the 'MULLERMANDEL' kick magnitude distribution", - " --muller-mandel-sigma-kick # kick scatter when using the 'MULLERMANDEL' kick magnitude distribution", + " --muller-mandel-sigma-kick-BH # BH kick scatter when using the 'MULLERMANDEL' kick magnitude distribution", + " --muller-mandel-sigma-kick-NS # NS kick scatter when using the 'MULLERMANDEL' kick magnitude distribution", " --neutrino-mass-loss-BH-formation-value", " --pisn-lower-limit # Minimum core mass for PISN [Msol]", " --pisn-upper-limit # Maximum core mass for PISN [Msol]", @@ -334,6 +340,7 @@ namespace yaml { " --common-envelope-formalism", " --common-envelope-lambda-prescription # Xu & Li 2010", " --common-envelope-mass-accretion-prescription", + " --common-envelope-second-stage-gamma-prescription", "", " ### TIDES", " --tides-prescription", @@ -343,6 +350,8 @@ namespace yaml { " --fryer-supernova-engine", " --kick-magnitude-distribution", " --kick-direction-distribution", + " --maltsev-fallback", + " --maltsev-mode", " --neutron-star-accretion-in-ce", " --neutron-star-equation-of-state", " --neutrino-mass-loss-BH-formation", @@ -361,6 +370,7 @@ namespace yaml { " --logfile-rlof-parameters", " --logfile-supernovae", " --logfile-switch-log", + " --logfile-system-snapshot-log", " --logfile-system-parameters", " --output-path", "",