Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e792561
Add scalar tensor with extra quantities as the output
diodaberjalan Feb 20, 2026
cab735e
Add safety net in root finder
diodaberjalan Feb 23, 2026
13ace6b
scalar-tensor update
diodaberjalan Mar 9, 2026
37beff0
Add inferences schemas
diodaberjalan Mar 9, 2026
68ee56e
feat: updated inference tests and added STT examples
diodaberjalan Mar 9, 2026
3d9bc1e
Delete examples/inference/st_smc_random_walk directory
diodaberjalan Mar 9, 2026
6a67033
Merge branch 'ST-inference-PR' into STT_inference
diodaberjalan Mar 9, 2026
f885598
Merge pull request #3 from diodaberjalan/STT_inference
diodaberjalan Mar 9, 2026
828bff4
Fix integration, adjusting test on ST, and updating example
diodaberjalan Mar 9, 2026
6c893f4
Fixing docs and tests
ThibeauWouters Mar 10, 2026
c1e0fb5
Fixing docs failing
ThibeauWouters Mar 10, 2026
41e386f
Addressing comments on PR #103
diodaberjalan Mar 10, 2026
b0a005e
Working on PR
ThibeauWouters Mar 23, 2026
38a9d8c
Working on PR
ThibeauWouters Mar 23, 2026
f4042bb
1. Updating NICER likelihood to handle phase-transition-like mass rad…
diodaberjalan Mar 26, 2026
76855c0
add mock MR data likelihood
diodaberjalan Mar 27, 2026
31cdcae
Rewind removing mock MR to state 38a9d8c
diodaberjalan Mar 30, 2026
65817f2
Merge branch 'nuclear-multimessenger-astronomy:main' into ST-inferenc…
diodaberjalan Apr 1, 2026
cee587c
1. Updating NICER likelihood to handle phase-transition-like mass rad…
diodaberjalan Mar 26, 2026
8555d9d
NICER likelihood: Change minus infinity to penalty value
diodaberjalan Apr 1, 2026
1d37bb7
pre-commit
diodaberjalan Apr 1, 2026
88fbfaf
removing mock data notebook
diodaberjalan Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,15 @@ dmypy.json
.pytype/

# Cython debug symbols
cython_debug/
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

lalsuite/

examples/inference/*/*/outdir/
12 changes: 2 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@ repos:
rev: 24.1.1
hooks:
- id: black
exclude: \.ipynb$
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.1.14'
hooks:
- id: ruff
args: ["--fix"]
- repo: https://github.com/nbQA-dev/nbQA
rev: 1.7.1
hooks:
- id: nbqa-black
exclude: ^docs/examples/
additional_dependencies: [ipython==8.12, black]
- id: nbqa-ruff
exclude: ^docs/examples/
args: ["--fix", "--ignore=I001"]
additional_dependencies: [ipython==8.12, ruff]
exclude: \.ipynb$
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

.. autosummary::

~FamilyData.extra
~FamilyData.lambdas
~FamilyData.log10pcs
~FamilyData.masses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

~TOVSolution.M
~TOVSolution.R
~TOVSolution.extra
~TOVSolution.k2


632 changes: 632 additions & 0 deletions docs/examples/eos_tov/EoS_beyondGR.ipynb

Large diffs are not rendered by default.

9 changes: 3 additions & 6 deletions docs/examples/eos_tov/automatic_differentiation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,10 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"gr_tov_solver = GRTOVSolver()\n",
"construct_family_lambda = lambda eos_data: gr_tov_solver.construct_family(eos_data, ndat=100, min_nsat=1.0)"
]
"source": "gr_tov_solver = GRTOVSolver()\nconstruct_family_lambda = lambda eos_data: gr_tov_solver.construct_family(eos_data, tov_params={}, ndat=100, min_nsat=1.0)\n"
},
{
"cell_type": "markdown",
Expand Down Expand Up @@ -814,4 +811,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
2 changes: 1 addition & 1 deletion docs/examples/eos_tov/eos_STtov.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "jester",
"display_name": "jestertov",
"language": "python",
"name": "python3"
},
Expand Down
9 changes: 3 additions & 6 deletions docs/examples/eos_tov/eos_tov.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,10 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"gr_tov_solver = GRTOVSolver()\n",
"sol: FamilyData = gr_tov_solver.construct_family(eos_data=eos_data, ndat = 200, min_nsat = 1.0)"
]
"source": "gr_tov_solver = GRTOVSolver()\nsol: FamilyData = gr_tov_solver.construct_family(eos_data=eos_data, tov_params={}, ndat = 200, min_nsat = 1.0)\n"
},
{
"cell_type": "markdown",
Expand Down Expand Up @@ -273,4 +270,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
1,414 changes: 1,414 additions & 0 deletions docs/examples/eos_tov/generate_mock.ipynb

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions docs/examples/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ Basic Examples

:doc:`eos_tov/eos_STtov`

**Scalar-tensor theory TOV solver**
Exploration of modified gravity theories using JESTER's scalar-tensor TOV solver to study deviations from general relativity.

:doc:`eos_tov/EoS_beyondGR`

.. toctree::
:hidden:

eos_tov/eos_tov
eos_tov/automatic_differentiation
eos_tov/eos_STtov
eos_tov/EoS_beyondGR
37 changes: 37 additions & 0 deletions examples/inference/st_smc_random_walk/chiEFT/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
seed: 44
dry_run: false
validate_only: false
eos:
type: metamodel_cse
ndat_metamodel: 100
nmax_nsat: 25.0
nb_CSE: 8
crust_name: DH
tov:
type: scalar_tensor
min_nsat_TOV: 0.75
ndat_TOV: 100
nb_masses: 100
prior:
specification_file: prior.prior
likelihoods:
- type: constraints_eos
enabled: true
- type: chieft
enabled: true
low_filename: ../../../../jesterTOV/inference/data/chiEFT/2402.04172/low.dat
high_filename: ../../../../jesterTOV/inference/data/chiEFT/2402.04172/high.dat
nb_n: 100
sampler:
type: smc-rw
n_particles: 2000
n_mcmc_steps: 10
target_ess: 0.9
random_walk_sigma: 0.1
output_dir: ./outdir/
postprocessing:
enabled: true
make_cornerplot: false
make_massradius: true
make_pressuredensity: true
debug_nans: true
13 changes: 13 additions & 0 deletions examples/inference/st_smc_random_walk/chiEFT/prior.prior
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
E_sat = UniformPrior(-16.1, -15.9, parameter_names=["E_sat"])
K_sat = UniformPrior(150.0, 300.0, parameter_names=["K_sat"])
Q_sat = UniformPrior(-500.0, 1100.0, parameter_names=["Q_sat"])
Z_sat = UniformPrior(-2500.0, 1500.0, parameter_names=["Z_sat"])
E_sym = UniformPrior(28.0, 45.0, parameter_names=["E_sym"])
L_sym = UniformPrior(10.0, 200.0, parameter_names=["L_sym"])
K_sym = UniformPrior(-400.0, 200.0, parameter_names=["K_sym"])
Q_sym = UniformPrior(-1000.0, 1500.0, parameter_names=["Q_sym"])
Z_sym = UniformPrior(-2000.0, 1500.0, parameter_names=["Z_sym"])
nbreak = UniformPrior(0.16, 0.32, parameter_names=["nbreak"])
beta_ST = UniformPrior(-6, 0, parameter_names=["beta_ST"])
phi_c = UniformPrior(0.999, 1, parameter_names=["phi_c"])
phi_inf_tgt = UniformPrior(0.999e-3, 1e-3, parameter_names=["phi_inf_tgt"])
28 changes: 28 additions & 0 deletions examples/inference/st_smc_random_walk/chiEFT/submit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash -l
#Set job requirements
#SBATCH -N 1
#SBATCH -n 1
#SBATCH -p gpu_h100
#SBATCH -t 10:00:00
#SBATCH --gpus-per-node=1
#SBATCH --cpus-per-gpu=1
#SBATCH --mem-per-gpu=40G
#SBATCH --output="./outdir/log_smc_rw.out"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does Slurm automatically create the parent directory for a #SBATCH --output path, or must that directory already exist before job submission?

💡 Result:

Slurm does not create missing parent directories for #SBATCH --output=... (or --error). The target directory must already exist and be writable at submit/run time; otherwise Slurm can’t open the file and the job will typically fail (often with an error like “No such file or directory”). (hpc-wiki.info)

Citations:


Create the output directory or use a path that is guaranteed to exist.

Slurm does not automatically create missing parent directories for #SBATCH --output. If ./outdir does not exist before job submission, Slurm will fail to open the log file and the job will fail. Either create the directory before submission or place the log file in a location that is guaranteed to exist.

Suggested change
-#SBATCH --output="./outdir/log_smc_rw.out"
+#SBATCH --output="log_smc_rw.out"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#SBATCH --output="./outdir/log_smc_rw.out"
`#SBATCH` --output="log_smc_rw.out"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/inference/st_smc_random_walk/chiEFT/submit.sh` at line 10, The
SBATCH output path uses ./outdir which may not exist; before submitting, ensure
the directory exists by creating it (e.g., run a directory-creation step prior
to SBATCH in submit.sh) or change the --output value to a guaranteed existing
path; update the submit.sh script so the directory for the --output (referenced
by the SBATCH --output="./outdir/log_smc_rw.out" directive) is created with
mkdir -p or replaced with an existing folder.

#SBATCH --job-name="SMC-RW-chiEFT"

now=$(date)
echo "$now"

# Loading modules
# module load 2024
# module load Python/3.10.4-GCCcore-11.3.0
source /home/twouters2/projects/jester_review/jester/.venv/bin/activate

# Display GPU name
nvidia-smi --query-gpu=name --format=csv,noheader

echo "=========================================="
echo "=== Running jester inference (SMC-RW) ==="
echo "=========================================="

run_jester_inference config.yaml
Comment on lines +19 to +28
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the example independent of one workstation and one current working directory.

The absolute virtualenv path on Line 19 only exists on one machine, and run_jester_inference config.yaml on Line 28 only works when the job starts in this directory. Resolve the script directory first, cd there, and make the venv path configurable so config.yaml, prior.prior, and the ChiEFT data paths stay valid.

Suggested change
+SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd -- "$SCRIPT_DIR/../../../../" && pwd)"
+cd "$SCRIPT_DIR"
+
- source /home/twouters2/projects/jester_review/jester/.venv/bin/activate
+JESTER_VENV="${JESTER_VENV:-$REPO_ROOT/.venv}"
+if [[ ! -f "$JESTER_VENV/bin/activate" ]]; then
+  echo "Virtualenv not found: $JESTER_VENV" >&2
+  exit 1
+fi
+source "$JESTER_VENV/bin/activate"
@@
-run_jester_inference config.yaml
+run_jester_inference config.yaml
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
source /home/twouters2/projects/jester_review/jester/.venv/bin/activate
# Display GPU name
nvidia-smi --query-gpu=name --format=csv,noheader
echo "=========================================="
echo "=== Running jester inference (SMC-RW) ==="
echo "=========================================="
run_jester_inference config.yaml
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd -- "$SCRIPT_DIR/../../../../" && pwd)"
cd "$SCRIPT_DIR"
JESTER_VENV="${JESTER_VENV:-$REPO_ROOT/.venv}"
if [[ ! -f "$JESTER_VENV/bin/activate" ]]; then
echo "Virtualenv not found: $JESTER_VENV" >&2
exit 1
fi
source "$JESTER_VENV/bin/activate"
# Display GPU name
nvidia-smi --query-gpu=name --format=csv,noheader
echo "=========================================="
echo "=== Running jester inference (SMC-RW) ==="
echo "=========================================="
run_jester_inference config.yaml
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/inference/st_smc_random_walk/chiEFT/submit.sh` around lines 19 - 28,
The script currently sources an absolute venv and runs run_jester_inference
assuming the CWD; change it to resolve the script directory and cd into it, make
the virtualenv path configurable via an environment variable (e.g., VENV with a
sensible default) and source "$VENV/bin/activate", and invoke
run_jester_inference with the config and data paths prefixed by the resolved
script directory (so references to config.yaml, prior.prior and ChiEFT data
paths remain valid regardless of the initial working directory). Ensure you
update the activate path and all file arguments to use the script-dir-relative
paths and preserve the nvidia-smi and echo steps.

2 changes: 2 additions & 0 deletions jesterTOV/inference/config/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from .schemas.tov import (
BaseTOVConfig,
GRTOVConfig,
ScalarTensorTOVConfig,
AnisotropyTOVConfig,
TOVConfig,
)
Expand Down Expand Up @@ -233,6 +234,7 @@ def validate_seed(cls, v: int) -> int:
# TOV
"BaseTOVConfig",
"GRTOVConfig",
"ScalarTensorTOVConfig",
"AnisotropyTOVConfig",
"TOVConfig",
# Likelihoods
Expand Down
29 changes: 28 additions & 1 deletion jesterTOV/inference/config/schemas/tov.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,32 @@ class GRTOVConfig(BaseTOVConfig):
type: Literal["gr"] = "gr" # type: ignore[override] # Literal["gr"] ⊂ str


class ScalarTensorTOVConfig(BaseTOVConfig):
"""Configuration for the Scalar-Tensor TOV solver.

Attributes
----------
type : Literal["scalar_tensor"]
TOV solver type identifier
beta_ST : float
Scalar-tensor coupling parameter beta_ST (default: 0.0)
phi_inf_tgt : float
Target asymptotic scalar field at infinity (default: 1e-3)
phi_c : float
Central scalar field value (default: 1.0)
"""

type: Literal["scalar_tensor"] = "scalar_tensor" # type: ignore[override]

beta_ST: float = Field(
default=0.0, description="Scalar-tensor coupling parameter beta_ST"
)
phi_inf_tgt: float = Field(
default=1e-3, description="Target asymptotic scalar field at infinity"
)
phi_c: float = Field(default=1.0, description="Central scalar field value")
Comment on lines +80 to +86
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### fetch_params definitions"
rg -n -A20 -B5 'def fetch_params\(' jesterTOV

echo
echo "### transform-side TOV parameter assembly"
sed -n '345,370p' jesterTOV/inference/transforms/transform.py

echo
echo "### scalar-tensor config schema"
sed -n '59,105p' jesterTOV/inference/config/schemas/tov.py

echo
echo "### scalar-tensor solver parameter consumption"
sed -n '626,672p' jesterTOV/tov/scalar_tensor.py

echo
echo "### inference-side references to the scalar-tensor parameter names"
rg -n -C3 '\b(beta_ST|phi_inf_tgt|phi_c)\b' jesterTOV/inference

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 8364


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### ScalarTensorTOVSolver.get_required_parameters() implementation"
rg -n -A10 'class ScalarTensorTOVSolver' jesterTOV/tov/scalar_tensor.py | head -50

echo
echo "### Full get_required_parameters for ScalarTensorTOVSolver"
sed -n '673,690p' jesterTOV/tov/scalar_tensor.py

echo
echo "### How run_inference.py uses the config and passes params to the solver"
rg -n -B5 -A15 'self.tov_solver' jesterTOV/inference/run_inference.py | head -80

echo
echo "### Config instantiation and schema usage in transforms"
rg -n -B3 -A10 'TOVConfig\|ScalarTensorTOVConfig' jesterTOV/inference/transforms/transform.py | head -60

echo
echo "### Parameter prior handling and merging"
rg -n -B5 -A10 'def setup_transforms' jesterTOV/inference/run_inference.py | head -60

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 816


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### Check if TOV solver is instantiated from config in run_inference"
rg -n 'tov_solver\|TOVSolver' jesterTOV/inference/run_inference.py | head -40

echo
echo "### Look for where solver config values would be merged into params"
rg -n -B5 -A10 'class.*Transform' jesterTOV/inference/transforms/transform.py | head -80

echo
echo "### Check setup_transforms or __init__ in Transform"
sed -n '1,150p' jesterTOV/inference/transforms/transform.py

echo
echo "### Check how params dict is built from prior"
rg -n -B3 -A8 'params.*prior\|prior.*params' jesterTOV/inference/run_inference.py

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 151


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### Examine run_inference.py structure"
wc -l jesterTOV/inference/run_inference.py
head -100 jesterTOV/inference/run_inference.py

echo
echo "### Look for Transform instantiation"
grep -n 'Transform' jesterTOV/inference/run_inference.py

echo
echo "### Examine Transform __init__"
sed -n '1,250p' jesterTOV/inference/transforms/transform.py

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 12999


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### _create_tov_solver implementation"
sed -n '250,450p' jesterTOV/inference/transforms/transform.py

echo
echo "### forward method and how params are used"
sed -n '300,380p' jesterTOV/inference/transforms/transform.py

echo
echo "### Check if config values are extracted and merged into params anywhere"
rg -n -B3 -A8 'tov_config\|ScalarTensorTOVConfig' jesterTOV/inference/transforms/transform.py

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 9984


🏁 Script executed:

#!/bin/bash
set -euo pipefixo pipefail

echo "### Check how fixed_params flow through to forward()"
sed -n '180,250p' jesterTOV/inference/base.py

echo
echo "### Check if tov_config values are ever used after _create_tov_solver"
rg -n 'tov_config\|tov_solver' jesterTOV/inference/transforms/transform.py | tail -40

echo
echo "### Verify the gap: config passed but not merged"
sed -n '235,260p' jesterTOV/inference/transforms/transform.py

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 136


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### Check how fixed_params flow through to forward()"
sed -n '180,250p' jesterTOV/inference/base.py

echo
echo "### Check if tov_config values are ever used after _create_tov_solver"
rg -n 'tov_config\|tov_solver' jesterTOV/inference/transforms/transform.py | tail -40

echo
echo "### Verify the gap: config passed but not merged"
sed -n '235,260p' jesterTOV/inference/transforms/transform.py

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 223


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### Find base.py location"
fd 'base\.py' jesterTOV/inference

echo
echo "### Check from_config method in transform.py more carefully"
sed -n '200,260p' jesterTOV/inference/transforms/transform.py

echo
echo "### Critical: After _create_tov_solver returns, are tov_config values used?"
sed -n '219,235p' jesterTOV/inference/transforms/transform.py

echo
echo "### Look for where params dict gets augmented with config defaults"
rg -n 'fixed_params\|tov_config' jesterTOV/inference/transforms/transform.py

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 2857


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### Trace: _create_tov_solver receives tov_config but what happens to its field values?"
sed -n '264,290p' jesterTOV/inference/transforms/transform.py

echo
echo "### The critical question: are tov_config field values EVER stored or merged anywhere?"
grep -n 'beta_ST\|phi_inf_tgt\|phi_c' jesterTOV/inference/transforms/transform.py

echo
echo "### How does run_inference.py call from_config?"
rg -n -B5 -A10 'from_config' jesterTOV/inference/run_inference.py | head -60

echo
echo "### What does the prior file provide? Are beta_ST etc. expected to be sampled?"
rg -n 'beta_ST\|phi_inf_tgt\|phi_c' jesterTOV/inference

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 1037


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### Check AnisotropyTOVConfig to see the pattern"
sed -n '85,110p' jesterTOV/inference/config/schemas/tov.py

echo
echo "### How are AnisotropyTOVSolver params handled in _create_tov_solver?"
sed -n '264,295p' jesterTOV/inference/transforms/transform.py

echo
echo "### CRITICAL: Check if config solver-specific params should be passed to solver __init__"
rg -n 'class.*TOVSolver' jesterTOV/tov/

echo
echo "### Check AnisotropyTOVSolver __init__ for reference"
rg -n -A15 'class AnisotropyTOVSolver' jesterTOV/tov/anisotropy.py | head -40

echo
echo "### Check ScalarTensorTOVSolver __init__"
rg -n -A15 'class ScalarTensorTOVSolver' jesterTOV/tov/scalar_tensor.py | head -40

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 3707


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### Final verification: Does AnisotropyTOVSolver.__init__ exist and take no params?"
rg -n -A20 'class AnisotropyTOVSolver' jesterTOV/tov/anisotropy.py | head -30

echo
echo "### Does ScalarTensorTOVSolver have __init__?"
sed -n '614,680p' jesterTOV/tov/scalar_tensor.py | grep -A10 '__init__\|def solve'

echo
echo "### Full AnisotropyTOVConfig docstring - note it says params come from PRIOR"
sed -n '85,102p' jesterTOV/inference/config/schemas/tov.py

echo
echo "### Compare: ScalarTensorTOVConfig docstring says fields are here with defaults"
sed -n '59,83p' jesterTOV/inference/config/schemas/tov.py

Repository: nuclear-multimessenger-astronomy/jester

Length of output: 3047


These scalar-tensor config fields are defined but not wired into tov_params—they will silently default via .get() rather than enforcing their schema values.

ScalarTensorTOVConfig defines beta_ST, phi_inf_tgt, and phi_c with Pydantic defaults, but _create_tov_solver() only uses the config to select the solver class; it never extracts or passes these field values to the solver or merges them into params.

In transform.py, tov_params = self.tov_solver.fetch_params(params) extracts these keys from the sampled prior dict. The solver then does tov_params.get("beta_ST", 0.0) etc., which silently falls back to the hardcoded defaults in solve() if the prior is missing these parameters—not the config defaults.

This differs from AnisotropyTOVConfig, whose docstring correctly states: "The six theory parameters are specified in the prior file, not here." The scalar-tensor config should either:

  1. Remove the field definitions and document these as required prior parameters, or
  2. Merge config defaults into params when keys are missing (similar to fixed_params handling)

Either way, add validation in run_inference.py (per prior validation guidelines) to ensure these parameters are present in the prior file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jesterTOV/inference/config/schemas/tov.py` around lines 76 - 82,
Scalar-tensor fields (beta_ST, phi_inf_tgt, phi_c) are declared on
ScalarTensorTOVConfig but never merged into tov_params, so the solver falls back
to hardcoded defaults; either remove these fields and document them as required
prior parameters, or implement merging of the config defaults into params (the
same way fixed_params are merged) so _create_tov_solver / transform.py
(tov_params = self.tov_solver.fetch_params(params)) passes them through and
solve() uses the config defaults rather than .get() fallbacks; additionally add
a validation in run_inference.py to assert the prior supplies beta_ST,
phi_inf_tgt, and phi_c (or to allow them to be provided via config if you keep
the fields) to prevent silent defaults.



class AnisotropyTOVConfig(BaseTOVConfig):
"""Configuration for the post-TOV solver with beyond-GR corrections.

Expand All @@ -72,7 +98,8 @@ class AnisotropyTOVConfig(BaseTOVConfig):
type: Literal["anisotropy"] = "anisotropy" # type: ignore[override] # Literal["anisotropy"] ⊂ str


# TOVConfig is a discriminated union of all available TOV solver configs.
TOVConfig = Annotated[
Union[GRTOVConfig, AnisotropyTOVConfig],
Union[GRTOVConfig, AnisotropyTOVConfig, ScalarTensorTOVConfig],
Discriminator("type"),
]
5 changes: 4 additions & 1 deletion jesterTOV/inference/transforms/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
SpectralEOSConfig,
BaseTOVConfig,
GRTOVConfig,
ScalarTensorTOVConfig,
AnisotropyTOVConfig,
)
from jesterTOV.inference.likelihoods.constraints import check_all_constraints
Expand Down Expand Up @@ -283,12 +284,14 @@ def _create_tov_solver(config: BaseTOVConfig) -> TOVSolverBase:
"""
if isinstance(config, GRTOVConfig):
return GRTOVSolver()
elif isinstance(config, ScalarTensorTOVConfig):
from jesterTOV.tov.scalar_tensor import ScalarTensorTOVSolver

return ScalarTensorTOVSolver()
elif isinstance(config, AnisotropyTOVConfig):
from jesterTOV.tov.anisotropy import AnisotropyTOVSolver

return AnisotropyTOVSolver()

else:
raise ValueError(f"Unknown TOV solver type: {type(config).__name__}")

Expand Down
61 changes: 46 additions & 15 deletions jesterTOV/tov/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

from abc import ABC, abstractmethod
from typing import Optional
import jax
import jax.numpy as jnp
from jaxtyping import Float, Array
Expand Down Expand Up @@ -103,6 +104,20 @@ def construct_family(
Returns:
FamilyData: Mass-radius-tidal curves in physical units
"""
# Evaluate EOS validity once for the entire family
is_valid_eos = (
jnp.all(eos_data.ps > 0)
& jnp.all(eos_data.hs > 0)
& jnp.all(eos_data.es > 0)
& jnp.all(eos_data.cs2 >= 0)
& jnp.all(eos_data.cs2 <= 1)
)

# # Poison the EOS data with NaNs if unphysical
# eos_data = jax.tree_util.tree_map(
# lambda x: jnp.where(is_valid_eos, x, jnp.nan), eos_data
# )

# Create central pressure grid
pc_min = self._get_pc_min(eos_data, min_nsat)
pc_max = self._get_pc_max(eos_data)
Expand All @@ -120,8 +135,13 @@ def solve_single_pc(pc):
radii: Float[Array, "ndat"] = solutions.R # type: ignore[assignment]
k2s: Float[Array, "ndat"] = solutions.k2 # type: ignore[assignment]

# Extract optional extra fields from solver-specific quantities
extra_fields: Optional[dict[str, Float[Array, "ndat"]]] = solutions.extra # type: ignore[assignment]

# Convert to physical units and compute tidal deformability
return self._create_family_data(pcs, masses, radii, k2s, ndat)
return self._create_family_data(
pcs, masses, radii, k2s, ndat, extra=extra_fields
)

def _get_pc_min(self, eos_data: EOSData, min_nsat: float) -> Float[Array, ""]:
"""
Expand Down Expand Up @@ -170,6 +190,7 @@ def _create_family_data(
radii: Float[Array, "ndat"],
k2s: Float[Array, "ndat"],
ndat: int,
extra: Optional[dict[str, Float[Array, "ndat"]]] = None,
) -> FamilyData:
"""
Shared post-processing: unit conversion, compactness limits, interpolation.
Expand All @@ -180,6 +201,7 @@ def _create_family_data(
radii: Radii [geometric units]
k2s: Love numbers [dimensionless]
ndat: Number of points for output grid
extra: Optional dictionary of solver-specific quantities (e.g., from ScalarTensorTOVSolver)

Returns:
FamilyData: Processed family curves in physical units
Expand All @@ -194,21 +216,30 @@ def _create_family_data(
# Calculate tidal deformability
lambdas = 2.0 / 3.0 * k2s * jnp.power(compactness, -5.0)

# Limit masses to be below MTOV (removes unstable branch)
pcs_lim, masses_lim, radii_lim, lambdas_lim = utils.limit_by_MTOV(
pcs, masses_solar, radii_km, lambdas
# Limit masses to be below MTOV and interpolate onto a uniform log(pc) grid on stable segments
pcs_lim, masses_lim, radii_lim, lambdas_lim = (
utils.limit_by_MTOV_and_interpolate(
pcs, masses_solar, radii_km, lambdas, ndat
)
)

# Get a mass grid and interpolate, since we might have some duplicate points
mass_grid = jnp.linspace(jnp.min(masses_lim), jnp.max(masses_lim), ndat)
radii_interp = jnp.interp(mass_grid, masses_lim, radii_lim)
lambdas_interp = jnp.interp(mass_grid, masses_lim, lambdas_lim)
pcs_interp = jnp.interp(mass_grid, masses_lim, pcs_lim)
log10pcs = jnp.log10(pcs_interp)
# @TODO: merge extras interpolation in a single call in above.
# Process extra solver-specific fields if provided
extra_processed: Optional[dict[str, Float[Array, "ndat"]]] = None
if extra is not None:
# Use tree_map to efficiently apply interpolation to all extra fields at once
extra_processed = jax.tree_util.tree_map(
lambda val: utils.limit_by_MTOV_and_interpolate(
pcs, masses_solar, radii_km, val, ndat
)[
3
], # Take only the interpolated fourth element (the 'lambda'-like array)
extra,
)

return FamilyData(
log10pcs=log10pcs,
masses=mass_grid,
radii=radii_interp,
lambdas=lambdas_interp,
log10pcs=jnp.log10(pcs_lim),
masses=masses_lim,
radii=radii_lim,
lambdas=lambdas_lim,
extra=extra_processed,
)
Loading
Loading