Merge pyodide_py into python_stdlib.zip. ### Description

Merges pyodide_py.tar into python_stdlib.zip. This reduces the number of network requests during the initialization.

The original intention of splitting pyodide_py.tar from pyodide.asm.data was to give users an option to provide custom files (#2023), which is now available by modifying python_stdlib.zip directly. So I think there is no reason to have both python_stdlib.zip and pyodide_py.tar.

### Checklists

- [x] Add a [CHANGELOG](https://github.com/pyodide/pyodide/blob/main/docs/project/changelog.md) entry
- [x] Add / update tests

from functools import reduce

import pytest
from pytest_pyodide import run_in_pyodide

NODE_XFAIL_REASON = (
    "galpy loads matplotlib and there are no supported matplotlib backends on node"
)

# Need to skip_refcount_check because we use matplotlib


def galpy_test_decorator(**kwargs):
    def dec(f):
        return reduce(
            lambda x, g: g(x),
            [
                pytest.mark.xfail_browsers(node=NODE_XFAIL_REASON, **kwargs),
                pytest.mark.skip_refcount_check,
            ],
            f,
        )

    return dec


@galpy_test_decorator(firefox="times out")
@run_in_pyodide(
    packages=[
        "galpy",
    ]
)
def test_integrate(selenium):
    import numpy
    from galpy.orbit import Orbit
    from galpy.potential import MWPotential2014

    ts = numpy.linspace(0.0, 100.0, 1001)
    o = Orbit()
    o.integrate(ts, MWPotential2014)
    assert (
        numpy.fabs(numpy.std(o.E(ts)) / numpy.mean(o.E(ts))) < 1e-10
    ), "Orbit integration does not conserve energy"
    return None


@galpy_test_decorator(firefox="times out")
@run_in_pyodide(
    packages=[
        "galpy",
    ]
)
def test_actionAngle(selenium):
    import numpy
    from galpy.orbit import Orbit
    from galpy.potential import MWPotential2014

    ts = numpy.linspace(0.0, 100.0, 1001)
    o = Orbit()
    o.integrate(ts, MWPotential2014)
    all_os = o(ts)
    jrs = all_os.jr(pot=MWPotential2014)
    jzs = all_os.jz(pot=MWPotential2014)
    assert (
        numpy.fabs(numpy.std(jrs) / numpy.mean(jrs)) < 1e-4
    ), "Actions not conserved during orbit integration"
    assert (
        numpy.fabs(numpy.std(jzs) / numpy.mean(jzs)) < 1e-3
    ), "Actions not conserved during orbit integration"
    return None


@galpy_test_decorator(firefox="times out")
@run_in_pyodide(
    packages=[
        "galpy",
    ]
)
def test_isotropic_hernquist_sigmar(selenium):
    import numpy
    from galpy import potential
    from galpy.df import isotropicHernquistdf, jeans

    def check_sigmar_against_jeans(
        samp, pot, tol, beta=0.0, dens=None, rmin=None, rmax=None, bins=31
    ):
        """Check that sigma_r(r) obtained from a sampling agrees with that
        coming from the Jeans equation
        Does this by logarithmically binning in r between rmin and rmax"""
        vrs = (samp.vR() * samp.R() + samp.vz() * samp.z()) / samp.r()
        logrs = numpy.log(samp.r())
        if rmin is None:
            numpy.exp(numpy.amin(logrs))
        if rmax is None:
            numpy.exp(numpy.amax(logrs))
        w, e = numpy.histogram(
            logrs,
            range=(numpy.log(rmin), numpy.log(rmax)),
            bins=bins,
            weights=numpy.ones_like(logrs),
        )
        mv2, _ = numpy.histogram(
            logrs,
            range=(numpy.log(rmin), numpy.log(rmax)),
            bins=bins,
            weights=vrs**2.0,
        )
        samp_sigr = numpy.sqrt(mv2 / w)
        brs = numpy.exp((numpy.roll(e, -1) + e)[:-1] / 2.0)
        for ii, br in enumerate(brs):
            assert (
                numpy.fabs(
                    samp_sigr[ii] / jeans.sigmar(pot, br, beta=beta, dens=dens) - 1.0
                )
                < tol
            ), "sigma_r(r) from samples does not agree with that obtained from the Jeans equation"
        return None

    pot = potential.HernquistPotential(amp=2.3, a=1.3)
    dfh = isotropicHernquistdf(pot=pot)
    numpy.random.seed(10)
    samp = dfh.sample(n=300000)
    tol = 0.05
    check_sigmar_against_jeans(
        samp,
        pot,
        tol,
        beta=0.0,
        rmin=pot._scale / 10.0,
        rmax=pot._scale * 10.0,
        bins=31,
    )
    return None

from functools import reduce

import pytest
from pytest_pyodide import run_in_pyodide

NODE_XFAIL_REASON = (
    "galpy loads matplotlib and there are no supported matplotlib backends on node"
)

# Need to skip_refcount_check because we use matplotlib


def galpy_test_decorator(**kwargs):
    def dec(f):
        return reduce(
            lambda x, g: g(x),
            [
                pytest.mark.xfail_browsers(node=NODE_XFAIL_REASON, **kwargs),
                pytest.mark.skip_refcount_check,
            ],
            f,
        )

    return dec


@galpy_test_decorator(firefox="times out")
@run_in_pyodide(
    packages=[
        "galpy",
    ]
)
def test_integrate(selenium):
    import numpy
    from galpy.orbit import Orbit
    from galpy.potential import MWPotential2014

    ts = numpy.linspace(0.0, 100.0, 1001)
    o = Orbit()
    o.integrate(ts, MWPotential2014)
    assert (
        numpy.fabs(numpy.std(o.E(ts)) / numpy.mean(o.E(ts))) < 1e-10
    ), "Orbit integration does not conserve energy"
    return None


@galpy_test_decorator(firefox="times out")
@run_in_pyodide(
    packages=[
        "galpy",
    ]
)
def test_actionAngle(selenium):
    import numpy
    from galpy.orbit import Orbit
    from galpy.potential import MWPotential2014

    ts = numpy.linspace(0.0, 100.0, 1001)
    o = Orbit()
    o.integrate(ts, MWPotential2014)
    all_os = o(ts)
    jrs = all_os.jr(pot=MWPotential2014)
    jzs = all_os.jz(pot=MWPotential2014)
    assert (
        numpy.fabs(numpy.std(jrs) / numpy.mean(jrs)) < 1e-4
    ), "Actions not conserved during orbit integration"
    assert (
        numpy.fabs(numpy.std(jzs) / numpy.mean(jzs)) < 1e-3
    ), "Actions not conserved during orbit integration"
    return None


@galpy_test_decorator(firefox="times out")
@run_in_pyodide(
    packages=[
        "galpy",
    ]
)
def test_isotropic_hernquist_sigmar(selenium):
    import numpy
    from galpy import potential
    from galpy.df import isotropicHernquistdf, jeans

    def check_sigmar_against_jeans(
        samp, pot, tol, beta=0.0, dens=None, rmin=None, rmax=None, bins=31
    ):
        """Check that sigma_r(r) obtained from a sampling agrees with that
        coming from the Jeans equation
        Does this by logarithmically binning in r between rmin and rmax"""
        vrs = (samp.vR() * samp.R() + samp.vz() * samp.z()) / samp.r()
        logrs = numpy.log(samp.r())
        if rmin is None:
            numpy.exp(numpy.amin(logrs))
        if rmax is None:
            numpy.exp(numpy.amax(logrs))
        w, e = numpy.histogram(
            logrs,
            range=(numpy.log(rmin), numpy.log(rmax)),
            bins=bins,
            weights=numpy.ones_like(logrs),
        )
        mv2, _ = numpy.histogram(
            logrs,
            range=(numpy.log(rmin), numpy.log(rmax)),
            bins=bins,
            weights=vrs**2.0,
        )
        samp_sigr = numpy.sqrt(mv2 / w)
        brs = numpy.exp((numpy.roll(e, -1) + e)[:-1] / 2.0)
        for ii, br in enumerate(brs):
            assert (
                numpy.fabs(
                    samp_sigr[ii] / jeans.sigmar(pot, br, beta=beta, dens=dens) - 1.0
                )
                < tol
            ), "sigma_r(r) from samples does not agree with that obtained from the Jeans equation"
        return None

    pot = potential.HernquistPotential(amp=2.3, a=1.3)
    dfh = isotropicHernquistdf(pot=pot)
    numpy.random.seed(10)
    samp = dfh.sample(n=300000)
    tol = 0.05
    check_sigmar_against_jeans(
        samp,
        pot,
        tol,
        beta=0.0,
        rmin=pot._scale / 10.0,
        rmax=pot._scale * 10.0,
        bins=31,
    )
    return None
