├── requirements.txt ├── pyMesa ├── __init__.py └── pyMesaUtils.py ├── setup.py ├── images └── logo.png ├── MANIFEST.in ├── pyproject.toml ├── mesa_models ├── utils.py ├── const.py ├── atm.py ├── chem.py ├── colors.py ├── neu.py ├── rates.py ├── eos.py ├── kap.py ├── net.py └── star.py ├── CITATION ├── .zenodo.json ├── setup.cfg ├── codemeta.json ├── .github └── workflows │ └── pypi.yml ├── .gitignore ├── README.md └── LICENSE /requirements.txt: -------------------------------------------------------------------------------- 1 | gfort2py>=2.6.2 2 | -------------------------------------------------------------------------------- /pyMesa/__init__.py: -------------------------------------------------------------------------------- 1 | from .pyMesaUtils import * -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup() 4 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjfarmer/pyMesa/HEAD/images/logo.png -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include README.md 3 | include LICENSE 4 | include CITATION 5 | include setup.py 6 | include setup.cfg 7 | include pyproject.toml 8 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"] 3 | 4 | build-backend = "setuptools.build_meta" 5 | 6 | [tool.setuptools_scm] 7 | 8 | 9 | -------------------------------------------------------------------------------- /mesa_models/utils.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | import sys 3 | 4 | utils_lib, utils_def = pym.loadMod("utils") 5 | 6 | utils_lib.utils_omp_get_max_threads() 7 | utils_lib.utils_omp_get_thread_num() 8 | -------------------------------------------------------------------------------- /CITATION: -------------------------------------------------------------------------------- 1 | @misc{pymesa, 2 | author = {Robert Farmer and 3 | Evan B. Bauer}, 4 | title = {pyMesa}, 5 | month = mar, 6 | year = 2018, 7 | doi = {10.5281/zenodo.1205271}, 8 | url = {https://doi.org/10.5281/zenodo.1205271} 9 | } 10 | -------------------------------------------------------------------------------- /mesa_models/const.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | 4 | const_lib,const_def = pym.loadMod("const") 5 | 6 | 7 | print(const_def.a2rad) 8 | print(const_def.thermohaline_mixing) 9 | print(const_def.mev_to_ergs) 10 | 11 | 12 | ierr=0 13 | const_lib.const_init(pym.MESA_DIR,ierr) 14 | print(const_def.mev_to_ergs) 15 | -------------------------------------------------------------------------------- /mesa_models/atm.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | const_lib, const_def = pym.loadMod("const") 4 | math_lib, _ = pym.loadMod("math") 5 | math_lib.math_init() 6 | ierr=0 7 | const_lib.const_init(pym.MESA_DIR,ierr) 8 | 9 | atm_lib, atm_def = pym.loadMod("atm") 10 | 11 | print(atm_lib.atm_l(5777.0,1.0).result) 12 | 13 | atm_lib.atm_shutdown() 14 | 15 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "creators": [ 3 | { 4 | "name": "Farmer, Robert", 5 | "orcid": "0000-0003-3441-7624" 6 | } 7 | ], 8 | "description": "Allows Python to interface with the MESA stellar evolution code.", 9 | "keywords": [ 10 | "Python", 11 | "Fortran", 12 | "MESA" 13 | ], 14 | "license": { 15 | "id": "GPL-2.0+" 16 | }, 17 | "title": "pyMesa" 18 | } 19 | 20 | -------------------------------------------------------------------------------- /mesa_models/chem.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | const_lib, const_def = pym.loadMod("const") 4 | math_lib, _ = pym.loadMod("math") 5 | math_lib.math_init() 6 | ierr=0 7 | const_lib.const_init(pym.MESA_DIR,ierr) 8 | 9 | chem_lib,chem_def = pym.loadMod("chem") 10 | 11 | 12 | chem_lib.chem_init('isotopes.data',ierr) 13 | 14 | 15 | for iso in ['c12','c13','c14']: 16 | print(f"{iso} {chem_lib.lodders03_element_atom_percent(iso).result}%") 17 | 18 | 19 | 20 | chem_lib.chem_shutdown() -------------------------------------------------------------------------------- /mesa_models/colors.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | import os 3 | import numpy as np 4 | 5 | const_lib, const_def = pym.loadMod("const") 6 | math_lib, _ = pym.loadMod("math") 7 | math_lib.math_init() 8 | ierr=0 9 | const_lib.const_init(pym.MESA_DIR,ierr) 10 | 11 | col_lib,col_def = pym.loadMod("colors") 12 | 13 | 14 | ierr = 0 15 | 16 | fnames = np.array(['lcb98cor.dat'],dtype='S') 17 | 18 | # Needs gfort2py >=2.2.1 19 | col_lib.colors_init(len(fnames),fnames,np.array([11]),ierr) 20 | 21 | 22 | 23 | print(col_lib.get_bc_by_name( 24 | 'V',np.log10(5777.0), 4.5, 0.0, ierr 25 | ).result 26 | ) 27 | 28 | 29 | col_lib.colors_shutdown() -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pyMesa 3 | description = "Python bindings for MESA (Modules for Experiments in Stellar Astrophysics)" 4 | long_description = file: README.md 5 | long_description_content_type = text/markdown 6 | author = Robert Farmer 7 | author_email = robert.j.farmer37@gmail.com 8 | url = https://github.com/rjfarmer/pyMesa 9 | license = GPLv2+ 10 | license_files = COPYING.txt 11 | platforms = unix, linux 12 | 13 | classifiers = 14 | Programming Language :: Python :: 3 15 | Programming Language :: Python :: 3 :: Only 16 | Programming Language :: Python :: 3.7 17 | Programming Language :: Python :: 3.8 18 | Programming Language :: Python :: 3.9 19 | Programming Language :: Python :: 3.10 20 | Programming Language :: Python :: 3.11 21 | 22 | [options] 23 | python_requires = >=3.7 24 | zip_safe = False 25 | packages=find: 26 | include_package_data = true 27 | -------------------------------------------------------------------------------- /codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "http://schema.org", 3 | "@type": "Code", 4 | "author": [ 5 | { 6 | "@id": "https://orcid.org/0000-0003-3441-7624", 7 | "@type": "Person", 8 | "email": "robert.j.farmer37@gmail.com", 9 | "name": "Robert Farmer" 10 | }, 11 | { 12 | "@id": "https://orcid.org/0000-0002-4791-6724", 13 | "@type": "Person", 14 | "email": "ebauer@physics.ucsb.edu", 15 | "name": "Evan Bauer" 16 | } 17 | ], 18 | "citation": "https://doi.org/10.5281/zenodo.846304", 19 | "codeRepository": "https://github.com/rjfarmer/pyMesa", 20 | "dateCreated": "2017-07-25", 21 | "description": "Python bindings for the 1-D stellar evolution code, MESA", 22 | "keywords": "python, fortran, MESA, bindings", 23 | "license": "https://opensource.org/licenses/GPL-2.0", 24 | "name": "pyMesa" 25 | } 26 | -------------------------------------------------------------------------------- /mesa_models/neu.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | import numpy as np 4 | 5 | math_lib, _ = pym.loadMod("math") 6 | math_lib.math_init() 7 | 8 | neu_lib,neu_def = pym.loadMod("neu") 9 | 10 | 11 | T=10**9.0 12 | log10_T=np.log10(T) 13 | Rho=10**9.0 14 | log10_Rho=np.log10(T) 15 | abar=1.0 16 | zbar=1.0 17 | log10_Tlim=7.5 18 | flags=np.zeros(neu_def.num_neu_types) 19 | flags[:]=True 20 | loss=np.zeros(neu_def.num_neu_rvs) 21 | sources=np.zeros((neu_def.num_neu_types,neu_def.num_neu_rvs)) 22 | info=0 23 | 24 | res = neu_lib.neu_get(T, log10_T, Rho, log10_Rho, abar, zbar, log10_Tlim, flags, loss, sources, info) 25 | 26 | types = [ 27 | ('Pair',neu_def.pair_neu_type-1), 28 | ('Plasma',neu_def.plas_neu_type-1), 29 | ('Photo',neu_def.phot_neu_type-1), 30 | ('Brem',neu_def.brem_neu_type-1), 31 | ('Reco',neu_def.reco_neu_type-1), 32 | ] 33 | 34 | for name,id in types: 35 | print(f"{name} = {res.args['loss'][id]}") 36 | 37 | 38 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish to pypi 2 | on: [push] 3 | 4 | jobs: 5 | pypi-publish: 6 | name: Upload release to PyPI 7 | runs-on: ubuntu-latest 8 | environment: 9 | name: pypi 10 | url: https://pypi.org/p/pyMesa 11 | permissions: 12 | id-token: write # IMPORTANT: this permission is mandatory for trusted publishing 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set up Python 16 | uses: actions/setup-python@v4 17 | with: 18 | cache: 'pip' 19 | 20 | - name: Install dependices 21 | run: | 22 | python -m pip install build 23 | python -m pip install -r requirements.txt 24 | - name: Build a package 25 | run: >- 26 | python3 -m build 27 | 28 | - name: Publish package distributions to PyPI 29 | uses: pypa/gh-action-pypi-publish@release/v1 30 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') -------------------------------------------------------------------------------- /mesa_models/rates.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | import numpy as np 4 | import os 5 | import matplotlib.pyplot as plt 6 | 7 | 8 | const_lib, const_def = pym.loadMod("const") 9 | math_lib, _ = pym.loadMod("math") 10 | math_lib.math_init() 11 | chem_lib, chem_def = pym.loadMod("chem") 12 | rates_lib, rates_def = pym.loadMod("rates") 13 | 14 | ierr=0 15 | 16 | const_lib.const_init(pym.MESA_DIR,ierr) 17 | chem_lib.chem_init('isotopes.data',ierr) 18 | 19 | rates_lib.rates_init('reactions.list','jina_reaclib_results_20171020_default', 20 | 'rate_tables',False,False,'','',pym.RATES_CACHE,ierr) 21 | 22 | ierr=0 23 | 24 | # Get raw rate 25 | 26 | rates_lib.show_reaction_rates_from_cache(os.path.join(pym.RATES_CACHE,'r_c12_ag_o16_1.bin'),ierr) 27 | 28 | c12o16_id=rates_lib.rates_reaction_id('r_c12_ag_o16').result 29 | 30 | logT=np.linspace(7.0,10.0,1000) 31 | r=[] 32 | for lt in logT: 33 | temp=10**lt 34 | tf={} 35 | res=rates_lib.eval_tfactors(tf, lt, temp) 36 | tf=res.args['tf'] 37 | raw_rate=0 38 | ierr=0 39 | res = rates_lib.get_raw_rate(c12o16_id, temp, tf, raw_rate, ierr) 40 | r.append(res.args['raw_rate']) 41 | 42 | plt.plot(logT,np.log10(r)) 43 | plt.show() 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /mesa_models/eos.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | import numpy as np 4 | 5 | eos_lib, eos_def = pym.loadMod("eos") 6 | const_lib, const_def = pym.loadMod("const") 7 | math_lib, _ = pym.loadMod("math") 8 | math_lib.math_init() 9 | chem_lib, chem_def = pym.loadMod("chem") 10 | 11 | ierr=0 12 | 13 | const_lib.const_init(pym.MESA_DIR,ierr) 14 | chem_lib.chem_init('isotopes.data',ierr) 15 | 16 | ierr=0 17 | 18 | eos_lib.eos_init(pym.EOSDT_CACHE, True, ierr) 19 | 20 | eos_inlist = 'eos.inlist' 21 | with open(eos_inlist,'w') as f: 22 | print('&eos',file=f) 23 | # Set options here 24 | print('/',file=f) 25 | 26 | ierr = 0 27 | res = eos_lib.alloc_eos_handle_using_inlist(eos_inlist, ierr) 28 | 29 | if res.args['ierr'] != 0: 30 | raise ValueError("Ierr not zero from alloc eos_handle") 31 | 32 | eos_handle = res.result 33 | 34 | # 50/50 mix of H/he 35 | species = 2 36 | chem_id = np.array([chem_def.ih1,chem_def.ihe4]) 37 | net_iso = np.array([1,2]) 38 | xa = np.array([0.5,0.5]) 39 | logRho = 8.0 40 | logT = 8.0 41 | 42 | res = np.zeros(eos_def.num_eos_basic_results) 43 | d_dlnRho = np.zeros(eos_def.num_eos_basic_results) 44 | d_dlnT = np.zeros(eos_def.num_eos_basic_results) 45 | d_dxa = np.zeros((eos_def.num_eos_basic_results,species)) 46 | 47 | eos_result = eos_lib.eosdt_get( 48 | eos_handle, species, chem_id, net_iso, xa, 49 | 10**logRho, logRho, 10**logT, logT, 50 | res, d_dlnRho, d_dlnT, d_dxa, 51 | ierr 52 | ) 53 | 54 | if res.args['ierr'] != 0: 55 | raise ValueError("Ierr not zero eosdt_get") 56 | 57 | # The EOS call returns the quantities we want in the "res" array. 58 | res = eos_result.args["res"] 59 | # These are indexed by indices that can be found in eos/public/eos_def.f90 60 | # We can get those indices with calls like this: 61 | i_lnE = eos_def.i_lne - 1 62 | # subtract 1 due to the difference between fortran and numpy indexing. 63 | 64 | IE = np.exp(res[i_lnE]) 65 | print("Internal Energy: ", IE, " erg/g") 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /mesa_models/kap.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | import numpy as np 4 | 5 | eos_lib, eos_def = pym.loadMod("eos") 6 | const_lib, const_def = pym.loadMod("const") 7 | math_lib, _ = pym.loadMod("math") 8 | math_lib.math_init() 9 | chem_lib, chem_def = pym.loadMod("chem") 10 | kap_lib,kap_def = pym.loadMod("kap") 11 | 12 | ierr=0 13 | 14 | 15 | const_lib.const_init('',ierr) 16 | chem_lib.chem_init('isotopes.data', ierr) 17 | 18 | eos_lib.eos_init(pym.EOSDT_CACHE, True, ierr) 19 | kap_lib.kap_init(True, pym.KAP_CACHE, ierr) 20 | 21 | 22 | # Create a kap namelist, this is the easiest way to set 23 | # the options 24 | 25 | kap_inlist = 'kap.inlist' 26 | with open(kap_inlist,'w') as f: 27 | print('&kap',file=f) 28 | # Set options here 29 | print('zbase = 0.02',file=f) 30 | print('/',file=f) 31 | 32 | ierr = 0 33 | res = kap_lib.alloc_kap_handle_using_inlist(kap_inlist, ierr) 34 | 35 | if res.args['ierr'] != 0: 36 | raise ValueError("Ierr not zero from alloc kap_handle") 37 | 38 | kap_handle = res.result 39 | 40 | 41 | # 50/50 mix of H/he 42 | species = 2 43 | chem_id = np.array([chem_def.ih1,chem_def.ihe4]) 44 | net_iso = np.array([1,2]) 45 | xa = np.array([0.5,0.5]) 46 | logRho = 8.0 47 | logT = 8.0 48 | 49 | # evaluate EOS for lnfree_e and eta and their derivatives 50 | 51 | eos_inlist = 'eos.inlist' 52 | with open(eos_inlist,'w') as f: 53 | print('&eos',file=f) 54 | # Set options here 55 | print('/',file=f) 56 | 57 | ierr = 0 58 | res = eos_lib.alloc_eos_handle_using_inlist(eos_inlist, ierr) 59 | 60 | if res.args['ierr'] != 0: 61 | raise ValueError("Ierr not zero from alloc eos_handle") 62 | 63 | eos_handle = res.result 64 | 65 | 66 | res = np.zeros(eos_def.num_eos_basic_results) 67 | d_dlnRho = np.zeros(eos_def.num_eos_basic_results) 68 | d_dlnT = np.zeros(eos_def.num_eos_basic_results) 69 | d_dxa = np.zeros((eos_def.num_eos_basic_results,species)) 70 | 71 | eos_result = eos_lib.eosdt_get( 72 | eos_handle, species, chem_id, net_iso, xa, 73 | 10**logRho, logRho, 10**logT, logT, 74 | res, d_dlnRho, d_dlnT, d_dxa, 75 | ierr 76 | ) 77 | 78 | if res.args['ierr'] != 0: 79 | raise ValueError("Ierr not zero eosdt_get") 80 | 81 | 82 | lnfree_e = eos_result.args['res'][eos_def.i_lnfree_e-1] 83 | d_lnfree_e_dlnRho = eos_result.args['d_dlnd'][eos_def.i_lnfree_e-1] 84 | d_lnfree_e_dlnT = eos_result.args['d_dlnt'][eos_def.i_lnfree_e-1] 85 | 86 | eta = eos_result.args['res'][eos_def.i_eta-1] 87 | d_eta_dlnRho = eos_result.args['d_dlnd'][eos_def.i_eta-1] 88 | d_eta_dlnT = eos_result.args['d_dlnt'][eos_def.i_eta-1] 89 | 90 | 91 | frac_Type2 = 0.0 92 | kap = 0.0 93 | kap_fracs = np.zeros(kap_def.num_kap_fracs) 94 | dlnkap_dlnRho = 0.0 95 | dlnkap_dlnT = 0.0 96 | dlnkap_dxa = np.zeros(species) 97 | ierr = 0 98 | 99 | kap_res = kap_lib.kap_get(kap_handle, species, chem_id, net_iso, xa, 100 | logRho, logT, 101 | lnfree_e, d_lnfree_e_dlnRho, d_lnfree_e_dlnT, 102 | eta, d_eta_dlnRho, d_eta_dlnT, 103 | kap_fracs, kap, dlnkap_dlnRho, dlnkap_dlnT, dlnkap_dxa, 104 | ierr) 105 | 106 | if res.args['ierr'] != 0: 107 | raise ValueError("Ierr not zero from kap_get") 108 | 109 | print(f"Opacity {kap_res.args['kap']}") 110 | 111 | 112 | kap_lib.kap_shutdown() 113 | 114 | -------------------------------------------------------------------------------- /mesa_models/net.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | 3 | import numpy as np 4 | import os 5 | 6 | eos_lib, eos_def = pym.loadMod("eos") 7 | const_lib, const_def = pym.loadMod("const") 8 | math_lib, _ = pym.loadMod("math") 9 | math_lib.math_init() 10 | chem_lib, chem_def = pym.loadMod("chem") 11 | net_lib, net_def = pym.loadMod("net") 12 | rates_lib, rates_def = pym.loadMod("rates") 13 | kap_lib, kap_def = pym.loadMod("kap") 14 | ion_lib, ion_def = pym.loadMod("ionization") 15 | 16 | ierr=0 17 | 18 | const_lib.const_init(pym.MESA_DIR,ierr) 19 | chem_lib.chem_init('isotopes.data',ierr) 20 | 21 | rates_lib.rates_init('reactions.list','jina_reaclib_results_20130213default2', 22 | 'rate_tables',False,False,'','','',ierr) 23 | 24 | kap_lib.kap_init('gs98','gs98_co','lowT_fa05_gs98',3.88,3.80,False,pym.KAP_CACHE,'',ierr) 25 | 26 | ion_lib.ionization_init('ion','',pym.ION_CACHE,False,ierr) 27 | net_lib.net_init(ierr) 28 | eos_lib.eos_init('mesa','','','',True,ierr) 29 | 30 | 31 | 32 | net_file = os.path.join(pym.NETS,'mesa_45.net') 33 | 34 | 35 | # Net setup 36 | net_lib.net_init(ierr) 37 | handle=net_lib.alloc_net_handle(ierr) 38 | net_lib.net_start_def(handle, ierr) 39 | net_lib.read_net_file(net_file, handle, ierr) 40 | net_lib.net_finish_def(handle, ierr) 41 | 42 | net_lib.net_set_logtcut(handle, -1,-1, ierr) 43 | net_lib.net_set_fe56ec_fake_factor(handle, 10**-7, 3.0*10**9, ierr) 44 | 45 | # Accessing the g pointer is broken 46 | # g={} 47 | # res = net_lib.net_ptr(handle, g, ierr) 48 | # g=res['g'] # Note this is only a copy of the pointer, changes wont propagate back to mesa 49 | 50 | species = net_lib.net_num_isos(handle, ierr) 51 | num_reactions = net_lib.net_num_reactions(handle, ierr) 52 | 53 | rates_reaction_id_max = rates_def.rates_reaction_id_max 54 | 55 | which_rates = np.zeros(rates_def.rates_reaction_id_max) 56 | reaction_id = np.zeros(num_reactions) 57 | which_rates[:] = rates_def.rates_jr_if_available 58 | #rates_lib.set_which_rates(ierr) 59 | net_lib.net_set_which_rates(handle, which_rates, ierr) 60 | net_lib.net_setup_tables(handle, '', ierr) 61 | 62 | # End net setup 63 | 64 | 65 | num_chem_isos = chem_def.num_chem_isos 66 | 67 | chem_id=np.zeros(num_chem_isos) 68 | net_iso_table=np.zeros(num_chem_isos) 69 | 70 | res=net_lib.get_chem_id_table(handle, species, chem_id, ierr) 71 | chem_id = res['chem_id'] 72 | 73 | res=net_lib.get_net_iso_table(handle, net_iso_table, ierr) 74 | net_iso = res['net_iso_table'] 75 | 76 | res=net_lib.get_reaction_id_table(handle, num_reactions, reaction_id, ierr) 77 | reaction_id = res['reaction_id'] 78 | 79 | reaction_table=np.zeros(rates_reaction_id_max) 80 | res=net_lib.get_net_reaction_table(handle, reaction_table, ierr) 81 | 82 | reaction_table = res['net_reaction_table'] 83 | 84 | #Setup reaction energies 85 | allQ = rates_def.std_reaction_qs 86 | allQneu = rates_def.std_reaction_neuqs 87 | 88 | reaction_Qs = np.zeros(num_reactions) 89 | reaction_neuQs = np.zeros(num_reactions) 90 | 91 | 92 | for i in range(num_reactions): 93 | reaction_Qs[i] = allQ[reaction_id[i]] 94 | reaction_neuQs[i] = allQneu[reaction_id[i]] 95 | 96 | 97 | 98 | # net_get function parameters 99 | just_dxdt = False 100 | n = {} 101 | num_isos = species 102 | x=np.zeros(num_isos) 103 | x[:]=10**-99 104 | 105 | x[net_lib.ih1] = 0.5 106 | x[net_lib.ihe4] = 0.5 107 | 108 | temp = 10**9 109 | log10temp = np.log10(temp) 110 | rho = 10**9 111 | log10rho = np.log10(rho) 112 | 113 | abar = 0.75 114 | zbar = 0.75 115 | z2bar = 0.5 116 | ye = 1.0 117 | 118 | eta = 0.0 119 | d_eta_dlnT = 0.0 120 | d_eta_dlnRho = 0.0 121 | 122 | rate_factors = np.zeros(num_reactions) 123 | rate_factors[:]=1.0 124 | weak_rate_factor = 1.0 125 | 126 | reuse_rate_raw = False 127 | reuse_rate_screened = False 128 | 129 | #Outs 130 | 131 | eps_nuc = 0.0 132 | d_eps_nuc_dT = 0.0 133 | d_eps_nuc_dRho = 0.0 134 | d_eps_nuc_dx = np.zeros(num_isos) 135 | dxdt = np.zeros(num_isos) 136 | d_dxdt_dRho = np.zeros(num_isos) 137 | d_dxdt_dT = np.zeros(num_isos) 138 | d_dxdt_dx = np.zeros((num_isos,num_isos)) 139 | 140 | eps_nuc_categories = np.zeros(chem_def.num_categories) 141 | eps_neu_total = 0.0 142 | screening_mode = 0 143 | theta_e_for_graboske_et_al = 0.0 144 | lwork = net_lib.net_work_size(handle, ierr) 145 | 146 | work = np.zeros(lwork) 147 | ierr = 0 148 | 149 | res = net_lib.net_get( 150 | handle, just_dxdt, n, num_isos, num_reactions, 151 | x, temp, log10temp, rho, log10rho, 152 | abar, zbar, z2bar, ye, eta, d_eta_dlnT, d_eta_dlnRho, 153 | rate_factors, weak_rate_factor, 154 | reaction_Qs, reaction_neuQs, reuse_rate_raw, reuse_rate_screened, 155 | eps_nuc, d_eps_nuc_dRho, d_eps_nuc_dT, d_eps_nuc_dx, 156 | dxdt, d_dxdt_dRho, d_dxdt_dT, d_dxdt_dx, 157 | screening_mode, theta_e_for_graboske_et_al, 158 | eps_nuc_categories, eps_neu_total, 159 | lwork, work, ierr) 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![pyMesa logo](images/logo.png) 2 | [![DOI](https://zenodo.org/badge/98320319.svg)](https://zenodo.org/badge/latestdoi/98320319) 3 | 4 | 5 | # pyMesa 6 | Allows python to interface with MESA stellar evolution code. 7 | 8 | 9 | ## Requirements: 10 | Note: pyMesa currently only works on linux, Macs will fail to build. 11 | 12 | Python dependencies can be installed with: 13 | 14 | `` 15 | python -m pip install -r requirements.txt 16 | `` 17 | 18 | We also need the following tool from installed by you system package manager or other means: 19 | 20 | `` 21 | chrpath 22 | `` 23 | 24 | 25 | ## Installing pyMesa 26 | 27 | The preferred way is via pip: 28 | 29 | `` 30 | python -m pip install --upgrade pyMesa 31 | `` 32 | 33 | 34 | ## Building MESA 35 | 36 | Go to ``$MESA_DIR/utils/makefile_header`` and find ``USE_SHARED=no`` and switch that to ``USE_SHARED=yes`` 37 | 38 | Then: 39 | 40 | ```` 41 | cd $MESA_DIR 42 | ./clean 43 | ./install 44 | ```` 45 | 46 | 47 | ## Supported MESA versions 48 | 49 | Any post github version: that is a version that starts with 'r2' or is from a git checkout. 50 | 51 | 52 | ## Running 53 | 54 | Make sure you set ``MESA_DIR`` and ``MESASDK_ROOT`` before starting Python. 55 | 56 | 57 | ## Usage 58 | 59 | Here is a basic example of talking to the ``const`` module. 60 | 61 | ````python 62 | import pyMesa as pym 63 | 64 | # pyMesa module defines a number of useful MESA paths as pym.SOMETHING. 65 | print(pym.MESA_DIR) # Print MESA_DIR 66 | 67 | # Loads the const module 68 | const_lib,const_def = pym.loadMod("const") 69 | 70 | # When calling a function we must either set the value we want (for intent(in/inout) variables) or an empty variable for intent(out). 71 | ierr=0 72 | # Calls a function 73 | res = const_lib.const_init(pym.MESA_DIR,ierr) 74 | 75 | # Functions and subroutines return a namedtuple 76 | 77 | print(res.result) # prints function result 78 | print(res.args) # prints all arguments 79 | 80 | 81 | # If the call was a subroutine then res is a dict with the intent out variables in there 82 | # else it contains the result of the function call 83 | 84 | # Accessing a variable defined in a module is simply: 85 | const_def.mev_to_ergs 86 | 87 | # If the variable is not a parameter then you can change it with: 88 | const_def.standard_cgrav = 5.0 89 | 90 | # When passing a derived type, you should pass a dict to the function (filled with anything you want set) 91 | x = {} 92 | # or 93 | x = {'a':1,'b':'abc','c':{'d':1}} 94 | 95 | # Functions accepting arrays should pass a numpy array of the size it expects (if the function allocates the array, then just pass None) 96 | x = np.zeros(size) 97 | 98 | ```` 99 | 100 | The folder ``mesa_models`` shows some examples of accessing different MESA modules. Note some may not work depending on whether MESA 101 | has changed the interface since the code was written. 102 | 103 | ## Procedure calls 104 | 105 | Calling a function or a subroutine is handled the same way: 106 | 107 | ````python 108 | result = module.my_function(arg1, arg2) 109 | ```` 110 | 111 | Where every arg should be provided either with the value to be inputted (intent(in) or intent(inout)) or a dummy empty provided for intent(out) values. 112 | 113 | The result of a procedure call is returned as a NamedTuple of ``(result, args)``. Thus a function result is accessed via: 114 | 115 | ````python 116 | result = module.my_function(arg1, arg2) 117 | 118 | #Function result 119 | result.result 120 | ```` 121 | 122 | While all the arguments (both those that change and those that don't) are returned via: 123 | 124 | ````python 125 | result = module.my_function(arg1, arg2) 126 | 127 | #Arguments 128 | result.args['arg1'] 129 | result.args['arg2'] 130 | ```` 131 | 132 | ## Arrays 133 | 134 | Remember that Fortran has 1-based arrays while Numpy uses 0-based. This comes 135 | up if you're accessing an array via a mesa constant: 136 | 137 | ````python 138 | mesa_array[mesa_module.i_mesa_const] 139 | ```` 140 | should instead be accessed as: 141 | 142 | ````python 143 | mesa_array[mesa_module.i_mesa_const-1] 144 | ```` 145 | 146 | ## Bug reports: 147 | 148 | Bug reports should go to the issue tracker on github. Please include mesa version, gfortran version, gfort2py version and pyMesa version 149 | 150 | ## Contributing 151 | 152 | In general, most of the development should go towards the gfort2py project to add new 153 | fortran features. This repository just handles building mesa for Python support. 154 | 155 | Bug reports, if mesa versions don't work, or new examples are welcome as either pull requests 156 | or issues on the GitHub tracker. 157 | 158 | ## Citations 159 | 160 | People who use pyMESA in papers should cite this using the zenodo link for the version they used. If you use pyMesa in a project (research or teaching), let me know and i can help advertise here (also useful for me to help 161 | with funding requests). The current version's citation is in the CITATION file. 162 | 163 | ## Known Projects using pyMesa 164 | 165 | [Poelarends et al 2017](https://ui.adsabs.harvard.edu/#abs/2017ApJ...850..197P/abstract) 166 | 167 | 168 | -------------------------------------------------------------------------------- /pyMesa/pyMesaUtils.py: -------------------------------------------------------------------------------- 1 | # Tool to build mesa with python support 2 | 3 | # Copyright (C) 2023 Robert Farmer 4 | 5 | #This file is part of pyMesa. 6 | 7 | #pyMesa is free software: you can redistribute it and/or modify 8 | #it under the terms of the GNU General Public License as published by 9 | #the Free Software Foundation, either version 2 of the License, or 10 | #(at your option) any later version. 11 | 12 | #pyMesa is distributed in the hope that it will be useful, 13 | #but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | #GNU General Public License for more details. 16 | 17 | #You should have received a copy of the GNU General Public License 18 | #along with pyMesa. If not, see . 19 | 20 | import gfort2py as gf 21 | import os 22 | import sys 23 | import shutil 24 | import subprocess 25 | 26 | 27 | #MESA DIR check and path set 28 | if "MESA_DIR" not in os.environ: 29 | raise ValueError("Must set MESA_DIR environment variable") 30 | else: 31 | MESA_DIR = os.environ.get('MESA_DIR') 32 | 33 | 34 | DATA_DIR = os.path.join(MESA_DIR,'data') 35 | LIB_DIR = os.path.join(MESA_DIR,'lib') 36 | INCLUDE_DIR = os.path.join(MESA_DIR,'include') 37 | 38 | RATES_DATA=os.path.join(DATA_DIR,'rates_data') 39 | EOSDT_DATA=os.path.join(DATA_DIR,'eosDT_data') 40 | EOSPT_DATA=os.path.join(DATA_DIR,'eosPT_data') 41 | ION_DATA=os.path.join(DATA_DIR,'ionization_data') 42 | KAP_DATA=os.path.join(DATA_DIR,'kap_data') 43 | 44 | NET_DATA=os.path.join(DATA_DIR,'net_data') 45 | 46 | RATES_CACHE=os.path.join(RATES_DATA,'cache') 47 | EOSDT_CACHE=os.path.join(EOSDT_DATA,'cache') 48 | EOSPT_CACHE=os.path.join(EOSPT_DATA,'cache') 49 | ION_CACHE=os.path.join(ION_DATA,'cache') 50 | KAP_CACHE=os.path.join(KAP_DATA,'cache') 51 | NETS=os.path.join(NET_DATA,'nets') 52 | 53 | MESASDK_ROOT=os.path.expandvars('$MESASDK_ROOT') 54 | 55 | with open(os.path.join(DATA_DIR,'version_number'),'r') as f: 56 | v=f.readline().strip() 57 | if not v.startswith('r2') and len(v)<7: 58 | raise ValueError(f"Unsupported MESA version {v}") 59 | 60 | 61 | p=sys.platform.lower() 62 | 63 | if p == "linux" or p == "linux2": 64 | LIB_EXT='so' 65 | elif p == "darwin": 66 | LIB_EXT="dylib" 67 | else: 68 | raise Exception(f"Platform not support {p}") 69 | 70 | # The one function you actually need 71 | def loadMod(module): 72 | 73 | MODULE_LIB = os.path.join(INCLUDE_DIR,f"{module}_lib.mod") 74 | MODULE_DEF = os.path.join(INCLUDE_DIR,f"{module}_def.mod") 75 | 76 | if module =='run_star_support': 77 | SHARED_LIB = os.path.join(LIB_DIR,f"librun_star_support.{LIB_EXT}") 78 | MODULE_LIB = os.path.join(INCLUDE_DIR,"run_star_support.mod") 79 | elif module =='run_star_extras': 80 | SHARED_LIB = os.path.join(LIB_DIR,f"librun_star_extras.{LIB_EXT}") 81 | MODULE_LIB = os.path.join(INCLUDE_DIR,"run_star_extras.mod") 82 | else: 83 | SHARED_LIB = os.path.join(LIB_DIR,f"lib{module}.{LIB_EXT}") 84 | 85 | x = None 86 | y = None 87 | 88 | try: 89 | x = gf.fFort(SHARED_LIB,MODULE_LIB) 90 | except FileNotFoundError: 91 | pass 92 | 93 | try: 94 | y = gf.fFort(SHARED_LIB,MODULE_DEF) 95 | except FileNotFoundError: 96 | pass 97 | 98 | return x, y 99 | 100 | 101 | def _buildRunStarSupport(): 102 | if os.path.exists(os.path.join(LIB_DIR,f"librun_star_support.{LIB_EXT}")): 103 | return 104 | 105 | cwd = os.getcwd() 106 | os.chdir(os.path.join(MESA_DIR,'star','make')) 107 | try: 108 | compile_cmd = ['gfortran -Wno-uninitialized -fno-range-check', 109 | '-fPIC -shared -fprotect-parens', 110 | '-fno-sign-zero -fbacktrace -ggdb', 111 | '-fopenmp -std=f2008 -Wno-error=tabs -I../public', 112 | '-I../private -I../../include', 113 | '-I'+os.path.join(MESASDK_ROOT,'include'), 114 | '-Wunused-value -W -Wno-compare-reals', 115 | '-Wno-unused-parameter -fimplicit-none -O2', 116 | '-ffree-form -x f95-cpp-input -I../defaults', 117 | '-I../job -I../other ../job/run_star_support.f90', 118 | '-Wl,-rpath=' + LIB_DIR, 119 | '-o librun_star_support.'+LIB_EXT, 120 | '-L'+LIB_DIR, 121 | '-lstar -lgyre -latm -lcolors -lnet -leos', 122 | '-lkap -lrates -lneu -lchem -linterp_2d -linterp_1d', 123 | '-lnum -lmtx -lconst -lutils -lrun_star_extras'] 124 | 125 | print(" ".join(compile_cmd)) 126 | x = subprocess.call(" ".join(compile_cmd),shell=True) 127 | if x: 128 | raise ValueError("Build run_star_support failed") 129 | shutil.copy2(f"librun_star_support.{LIB_EXT}",os.path.join(LIB_DIR,f"librun_star_support.{LIB_EXT}")) 130 | shutil.copy2('run_star_support.mod',os.path.join(INCLUDE_DIR,'run_star_support.mod')) 131 | except: 132 | raise 133 | finally: 134 | os.chdir(cwd) 135 | 136 | os.chdir(LIB_DIR) 137 | _checkcrpath() 138 | try: 139 | x = subprocess.call(f"chrpath -r librun_star_support.{LIB_EXT}",shell=True) 140 | if x: 141 | raise ValueError("chrpath failed") 142 | except: 143 | raise 144 | finally: 145 | os.chdir(cwd) 146 | 147 | print("Built run_star_support") 148 | 149 | 150 | def _buildRunStarExtras(rse=None): 151 | if os.path.exists(os.path.join(LIB_DIR,f"librun_star_extras.{LIB_EXT}")): 152 | return 153 | 154 | filename = 'run_star_extras.f' 155 | output = os.path.join(MESA_DIR,'star','make',filename) 156 | if rse is None: 157 | with open(output,'w') as f: 158 | print('module run_star_extras',file=f) 159 | print('use star_lib',file=f) 160 | print('use star_def',file=f) 161 | print('use const_def',file=f) 162 | print('implicit none',file=f) 163 | print('contains',file=f) 164 | print('include "standard_run_star_extras.inc"',file=f) 165 | print('end module run_star_extras',file=f) 166 | else: 167 | shutil.copy2(rse,output) 168 | 169 | cwd = os.getcwd() 170 | os.chdir(os.path.join(MESA_DIR,'star','make')) 171 | try: 172 | compile_cmd = ['gfortran -Wno-uninitialized -fno-range-check', 173 | '-fPIC -shared -fprotect-parens', 174 | '-fno-sign-zero -fbacktrace -ggdb', 175 | '-fopenmp -std=f2008 -Wno-error=tabs -I../public', 176 | '-I../private -I../../include', 177 | '-I'+os.path.join(MESASDK_ROOT,'include'), 178 | '-Wunused-value -W -Wno-compare-reals', 179 | '-Wno-unused-parameter -fimplicit-none -O2', 180 | '-ffree-form -x f95-cpp-input -I../defaults', 181 | '-I../job -I../other', 182 | filename, 183 | '-Wl,-rpath=' + LIB_DIR, 184 | '-o librun_star_extras.'+LIB_EXT, 185 | '-L'+LIB_DIR, 186 | '-lstar -lconst'] 187 | 188 | print(" ".join(compile_cmd)) 189 | x = subprocess.call(" ".join(compile_cmd),shell=True) 190 | if x: 191 | raise ValueError("Build run_star_extras failed") 192 | shutil.copy2(f"librun_star_extras.{LIB_EXT}",os.path.join(LIB_DIR,f"librun_star_extras.{LIB_EXT}")) 193 | shutil.copy2('run_star_extras.mod',os.path.join(INCLUDE_DIR,'run_star_extras.mod')) 194 | except: 195 | raise 196 | finally: 197 | os.chdir(cwd) 198 | 199 | os.chdir(LIB_DIR) 200 | _checkcrpath() 201 | try: 202 | x = subprocess.call(f"chrpath -r librun_star_extras.{LIB_EXT}",shell=True) 203 | if x: 204 | raise ValueError("chrpath failed") 205 | except: 206 | raise 207 | finally: 208 | os.chdir(cwd) 209 | 210 | print("Built run_star_extras") 211 | 212 | class MesaError(Exception): 213 | pass 214 | 215 | 216 | def _checkcrpath(): 217 | res = subprocess.call(["command","-v","chrpath"]) 218 | if res: 219 | raise ValueError("Please install chrpath") 220 | 221 | 222 | def make_basic_inlist(): 223 | with open('inlist','w') as f: 224 | print('&star_job',file=f) 225 | print('/',file=f) 226 | print('&controls',file=f) 227 | print('/',file=f) 228 | print('&pgstar',file=f) 229 | print('/',file=f) 230 | 231 | 232 | def mesa_init(): 233 | _buildRunStarExtras() 234 | _buildRunStarSupport() 235 | -------------------------------------------------------------------------------- /mesa_models/star.py: -------------------------------------------------------------------------------- 1 | import pyMesa as pym 2 | import numpy as np 3 | import os 4 | import tempfile 5 | 6 | class pyStar(object): 7 | def __init__(self, rse = None): 8 | self.star_lib, self.star_def = pym.loadMod("star") 9 | 10 | self.rse, _ = pym.loadMod('run_star_extras') 11 | self.star, _ = pym.loadMod("run_star_support") 12 | 13 | self.just_did_backup = False 14 | self.first_try = True 15 | self.continue_evolve_loop = True 16 | 17 | self.star_id = 0 18 | self.inlist = 'inlist' 19 | 20 | self.controls = {} 21 | self.star_job = {} 22 | self._to_be_added_ctrls = {} 23 | 24 | self.hist_names = [] 25 | self.prof_names = [] 26 | self.hist_data = [] 27 | self.prof_data = [] 28 | 29 | 30 | def error_check(self, res): 31 | if isinstance(res,dict) and 'ierr' in res: 32 | if res['ierr'] is not 0: 33 | raise pym.MesaError('Non zero ierr='+str(res['ierr'])) 34 | else: 35 | if int(res) != 0: 36 | raise pym.MesaError('Non zero ierr='+str(res)) 37 | 38 | def new_star(self, inlist='inlist'): 39 | res = self.star_lib.alloc_star(self.star_id,0) 40 | self.error_check(res) 41 | self.star_id = res['id'] 42 | if self.star_id <= 0: 43 | raise ValueError("New star init failed") 44 | self.inlist = inlist 45 | res = self.star.read_star_job_id(self.star_id, self.inlist, 0) 46 | self.error_check(res) 47 | res = self.star.star_setup(self.star_id, self.inlist, 0) 48 | self.error_check(res) 49 | 50 | def before_evolve_loop(self): 51 | res = self.star.before_evolve_loop(False,True,False, 52 | self.star.null_binary_controls,self.rse.extras_controls, 53 | 0,self.inlist,'restart_photo',True,0,self.star_id,0) 54 | self.error_check(res) 55 | 56 | def single_step(self): 57 | res = self.star.star_evolve_step(self.star_id, self.first_try, self.just_did_backup) 58 | return self.check_step(res) 59 | 60 | def model_number(self): 61 | mod_num = self.star_lib.get_model_number(self.star_id, 0) 62 | return mod_num 63 | 64 | def before_step_loop(self): 65 | res = self.star.before_step_loop(self.star_id, 0) 66 | self.error_check(res) 67 | 68 | def star_check_model(self): 69 | return self.star_lib.star_check_model(self.star_id) 70 | 71 | def star_pick_next_timestep(self): 72 | return self.star_lib.star_pick_next_timestep(self.star_id) 73 | 74 | def extras_check_model(self): 75 | return self.star.extras_check_model(self.star_id, 0) 76 | 77 | def check_step(self, step_result): 78 | keep_going = False 79 | if step_result == self.star_def.keep_going: 80 | step_result = self.star_lib.star_check_model(self.star_id) 81 | if step_result == self.star_def.keep_going: 82 | step_result = self.rse.extras_check_model(self.star_id) 83 | if step_result == self.star_def.keep_going: 84 | step_result = self.star_lib.star_pick_next_timestep(self.star_id) 85 | if step_result == self.star_def.keep_going: 86 | keep_going = True 87 | 88 | if keep_going: # End step normally 89 | return False 90 | 91 | if step_result == self.star_def.redo: 92 | step_result = self.star_lib.star_prepare_to_redo(self.star_id) 93 | if step_result == self.star_def.retry: 94 | step_result = self.star_lib.star_prepare_to_retry(self.star_id) 95 | if step_result == self.star_def.backup: 96 | step_result = self.star_lib.star_do1_backup(self.star_id) 97 | self.just_did_backup = True 98 | else: 99 | self.just_did_backup = False 100 | if step_result == self.star_def.terminate: 101 | self.continue_evolve_loop = False 102 | return False 103 | self.first_try = False 104 | return True 105 | 106 | def step(self): 107 | while self.single_step(): 108 | if not self.continue_evolve_loop: 109 | return False 110 | return True 111 | 112 | def single_evolve(self): 113 | if not self.continue_evolve_loop: 114 | return False 115 | 116 | result = 0 117 | res = 0 118 | self.first_try = True 119 | self.just_did_backup = False 120 | self.single_step() 121 | 122 | res = self.star.after_step_loop(self.star_id, self.inlist, False, result, 0) 123 | self.error_check(res) 124 | res = res['result'] 125 | 126 | if res != self.star_def.keep_going: 127 | if res != self.star_def.terminate: 128 | self.continue_evolve_loop = False 129 | raise ValueError("Something went wrong") 130 | else: 131 | # Need to check s%result_reason 132 | res = self.star.terminate_normal_evolve_loop(self.star_id, 0, False, res, 0) 133 | self.error_check(res) 134 | self.continue_evolve_loop = False 135 | return False 136 | 137 | self.save() 138 | 139 | self._pysave() 140 | 141 | return True 142 | 143 | def _pysave(self): 144 | if len(self.hist_names): 145 | self.hist_data.append({'model_number':self.model_number()}) 146 | for i in self.hist_names: 147 | self.hist_data[-1][i] = self.get_hist(i) 148 | if len(self.prof_names): 149 | self.prof_data.append({'model_number':self.model_number()}) 150 | for i in self.prof_names: 151 | self.prof_data[-1][i] = self.get_prof_nz(i) 152 | 153 | 154 | def save(self): 155 | self.star.do_saves(self.star_id, 0) 156 | 157 | def evolve(self): 158 | self.before_evolve_loop() 159 | while self.single_evolve(): 160 | if not self.continue_evolve_loop: 161 | return False 162 | self.after_evolve_loop() 163 | 164 | def after_evolve_loop(self): 165 | res = self.star.after_evolve_loop(self.star_id, True, 0) 166 | self.error_check(res) 167 | 168 | def destroy_star(self): 169 | self.star_lib.free_star(self.star_id, 0) 170 | 171 | def load_inlist(self, inlist='inlist'): 172 | self.inlist = inlist 173 | self.load_star_job(self.inlist) 174 | self.load_controls(self.inlist) 175 | 176 | def load_star_job(self, inlist): 177 | self.star_lib.read_star_job_id(self.star_id, inlist, 0) 178 | res = self.star_lib.star_setup(self.star_id, inlist, 0) 179 | self.error_check(res) 180 | 181 | def load_controls(self, inlist): 182 | self.star_lib.star_read_controls(self.star_id, inlist, 0) 183 | 184 | def get_hist(self, name): 185 | return self.star_lib.star_get_history_output_by_id(self.star_id, name) 186 | 187 | def nz(self): 188 | return int(self.get_hist('num_zones')) 189 | 190 | def get_prof(self, name, zone): 191 | nz = self.nz() 192 | if zone > nz: 193 | raise ValueError("Zones out of range") 194 | return self.star_lib.star_get_profile_output_by_id(self.star_id,name,zone) 195 | 196 | def get_prof_nz(self, name): 197 | nz = self.nz() 198 | output = np.zeros(nz+1) 199 | for i in range(1,nz+1): 200 | output[i] = self.star_lib.star_get_profile_output_by_id(self.star_id,name,i) 201 | return output 202 | 203 | 204 | def dump_controls(self, fname_star, fname_controls): 205 | self.star_lib.write_star_job_id(self.star_id, fname_star, 0) 206 | self.star_lib.star_write_controls(self.star_id, fname_controls, 0) 207 | 208 | def parse_inlist(self, fname): 209 | opts = {} 210 | with open(fname,'r') as f: 211 | lines = f.readlines() 212 | 213 | for i in lines[1:-2]: 214 | line = i.strip().replace(',','').split('=') 215 | key = line[0].strip() 216 | value = ''.join(line[1:]).strip() # Handle strings with = in them 217 | if '"' in value or len(value)==0: 218 | value = value.replace(" ","") 219 | t = str 220 | elif 'T' in value: 221 | value = True 222 | t = bool 223 | elif 'F' in value: 224 | value = False 225 | t = bool 226 | elif '*' in value or value.count('.')>1: 227 | t = None 228 | pass # Arrays handle later 229 | elif value.count('.')==1: 230 | value = float(value) 231 | t = float 232 | else: 233 | value = int(value) 234 | t = int 235 | key = key.lower() 236 | opts[key] = {'value':value,'type':t} 237 | return opts 238 | 239 | def read_inlists(self): 240 | _, fname_star = tempfile.mkstemp() 241 | _, fname_controls = tempfile.mkstemp() 242 | self.dump_controls(fname_star,fname_controls) 243 | self.controls = self.parse_inlist(fname_controls) 244 | self.star_job = self.parse_inlist(fname_star) 245 | os.remove(fname_star) 246 | os.remove(fname_controls) 247 | 248 | def add_control(self, name, value): 249 | if not len(self.controls): 250 | self.read_inlists() 251 | 252 | if name not in self.controls: 253 | raise AttributeError("Not valid control parameter") 254 | 255 | _, fname = tempfile.mkstemp() 256 | with open(fname,'w') as f: 257 | print('&controls',file=f) 258 | for key, value in self._to_be_added_ctrls: 259 | print(str(name),' = ',self.controls[key]['type'](value),file=f) 260 | print('/',file=f) 261 | self.load_controls(fname) 262 | os.remove(fname) 263 | self.read_inlists() 264 | 265 | def add_star_job(self, name, value): 266 | if not len(self.star_job): 267 | self.read_inlists() 268 | 269 | if name not in self.star_job: 270 | raise AttributeError("Not valid star_job parameter") 271 | 272 | _, fname = tempfile.mkstemp() 273 | with open(fname,'w') as f: 274 | print('&star_job',file=f) 275 | print(str(name),' = ',self.star_job[name]['type'](value),file=f) 276 | print('/',file=f) 277 | self.load_star_job(fname) 278 | os.remove(fname) 279 | self.read_inlists() 280 | 281 | 282 | def add_hist(self, name): 283 | self.hist_names.append(name) 284 | 285 | def add_prof(sef, name): 286 | self.prof_names.append(name) 287 | 288 | def get_dt(self): 289 | res = self.star.get_dt_next(self.star_id, 0, 0) 290 | self.error_check(res) 291 | return res['dt'] 292 | 293 | def set_dt(self, dt): 294 | res = self.star.set_dt_next(self.star_id, dt, 0) 295 | 296 | def __del__(self): 297 | self.star.free_star(self.star_id,0) 298 | 299 | 300 | def basic(): 301 | # Creates an empty inlist 302 | #pym.make_basic_inlist() # Or have a file in cwd called 'inlist' 303 | 304 | # Init 305 | s = pyStar() 306 | 307 | # Init new star 308 | s.new_star() 309 | s.evolve() # Run till end 310 | s.get_hist('star_age') 311 | s.get_prof('dm',1) 312 | # Might need to ctrl+c to stop the run 313 | mass=s.get_prof_nz('mass') 314 | temp=s.get_prof_nz('logT') 315 | import matplotlib.pyplot as plt 316 | plt.plot(mass,temp) 317 | plt.show() 318 | 319 | #basic() 320 | 321 | 322 | def setinlist(): 323 | s = pyStar() 324 | s.new_star() 325 | s.before_evolve_loop() 326 | s.single_evolve() # One step 327 | print(s.get_hist('star_age')) 328 | print(s.get_prof('dm',1)) 329 | print(s.get_hist('star_mass')) 330 | print(s.controls['initial_mass']) 331 | print(s.get_dt()) 332 | s.set_dt(s.get_dt()/2.0) 333 | print(s.get_dt()) 334 | s.star.star_set_v_flag(s.star_id, True, 0) # Can call any star_lib function that takes id instead of s 335 | s.single_evolve() # One step 336 | s.after_evolve_loop() # End evolution 337 | 338 | setinlist() 339 | 340 | 341 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------