generate.py

back to table · edit · history · where entries came from · files · download

3598 bytes, as of the version from 2026-10-30 06:27 (current). Recorded here, not run.

"""Tidal Love numbers of Newtonian polytropic stars -- numberdb.org/T619.

Run with SageMath (install numberdb once using sage -pip install numberdb):
    sage -python generate.py           # recompute and verify every stored row
    sage -python generate.py --fill    # fill this existing draft
Download tidal_solver.py and the attached files listed in `files` alongside
this script. Draft access needs NUMBERDB_API_KEY. The campaign wrapper accepts
a key on stdin when NUMBERDB_KEY_FROM_STDIN=1. --fill does not offer or publish.

The apsidal convention is half the induced/applied potential ratio. The n=0
surface density jump gives 3/[4(ell-1)] exactly. The n=1 expressions use Arb.
Other values use original-potential Riccati integration in t=sqrt(theta).
Two runs at 150/210 working decimal digits also change order (160/210),
step cap (1/12,1/18) and matching radius (1/2,2/3). Agreement is heuristic:
ODE truncation, propagation and endpoint errors are not rigorously bounded.
Only 100 significant digits are stored. No cached numerical input supplies
value(); its in-memory cache merely shares each background between ell=2,3.
See argument-selection.md for the mixed n groups and discrete ell selection.
The solver supports half-integer n, not every rational n in the domain.
"""
import os,sys
from functools import lru_cache
import numberdb.sage as numberdb
from sage.rings.real_arb import RealBallField
from sage.rings.rational_field import QQ
from tidal_solver import integrate

INDICES=('0','1/2','1','3/2','2','5/2','3','7/2','4','9/2')
CONFIG={150:(160,12,'1/2'),210:(210,18,'2/3')}
@lru_cache(maxsize=24)
def solution(n,working):return integrate(n,working,*CONFIG[working])

class PolytropicLove(numberdb.Generator):
    table='T619'
    parameters=('n','ell')
    type='R'
    digits=100
    rigour='heuristic (agreement-checked)'
    files=('generate.py','tidal_solver.py','compute.py','radial_check.py','readback_checks.py',
           'dry_run.py','check.py','argument-selection.md','numerical-evidence.json',
           'radial-evidence.json','skill-manifest.json','skill-evidence.json','source-manifest.json')
    def enumerate(self):
        for n in INDICES:
            for ell in ('2','3'):yield {'n':n,'ell':ell}
    def value(self,params,digits):
        n=str(QQ(params['n']));ell=int(params['ell'])
        if n not in INDICES or ell not in (2,3):raise ValueError('Use selected indices and ell=2,3.')
        if digits>100:raise ValueError('Precision policy checked for at most 100 digits.')
        if n=='0':
            return {'number':QQ(3)/(4*(ell-1)),'comment':'The incompressible model includes the surface density jump.'}
        if n=='1':
            B=RealBallField(numberdb.bits(digits,losing=80));p=B.pi()**2
            val=(15-p)/(2*p) if ell==2 else (105-10*p)/(6*p)
            return {'number':val,'comment':r'The linear Lane–Emden model has $\theta_1(\xi)=\sin(\xi)/\xi$.'}
        return numberdb.agreeing(lambda working:str(solution(n,working)[0][ell-2]),at=(150,210))

if __name__=='__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
        token=sys.stdin.read().strip()
        if '=' in token and token.split('=',1)[0].replace('_','').isalnum():token=token.split('=',1)[1].strip().strip('\"\'')
        os.environ['NUMBERDB_API_KEY']=token
    g=PolytropicLove()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH')=='1':print(g.publish(message='Add twenty Newtonian apsidal constants with analytic and independent radial checks.'))
    else:
        r=g.verify(sample=None);print(r);sys.exit(0 if r.ok else 1)