Generar una distribución uniforme en el cielo

¿Cómo generar una distribución uniforme de estrellas en el cielo? Lo que quiero es hacer una simulación de puntos aleatorios siguiendo una distribución uniforme en una parte del cielo (asumiendo que tenemos la misma cantidad de estrellas en cualquier dirección). El problema es cómo hago eso debido al cos (dec) en el RA.

lo que hice en python es esto

import numpy as np
RA = np.random.uniform(-180,180)
Dec = np.random.uniform(-90,90)

Pero es totalmente falso, ya que tendremos más puntos en los polos (si hago muchos puntos, esto claramente no será uniforme)...

¡Muchas gracias por adelantado!

¿Sobre qué DEC=Arccos(np.random.uniform(-90,90))?
¡Gracias por la edición! No, no lo creo porque arccos se define entre -1 y 1, pero probablemente sea algo en esta dirección.
Algo como dec=np.arcsin(np.random.uniform(-1,1)) se ve un poco mejor, pero ¿hay alguien que pueda confirmarme si es matemáticamente correcto o no?
Iría arccosporque tiene más sentido (la longitud de una línea de declinación es proporcional a cos, no sina ), pero supongo que arcsinproduce la misma distribución.

Respuestas (4)

Se pueden generar puntos aleatorios en la superficie de una esfera permitiendo que el ángulo azimutal ϕ para tomar un valor aleatorio uniformemente distribuido entre 0 y 2 π . Para convertir esto a RA en grados, multiplique por 180 / π . Para convertir a horas, minutos y segundos se divide el ϕ en grados por 15, lo que da las horas, divide el resto por 60 que da los minutos y luego divide el resto por 60 para dar los segundos.

El ángulo de declinación d no se distribuye uniformemente, porque el área cubierta en un ángulo d va como porque d .

Tenga en cuenta la posible confusión aquí entre la declinación y el ángulo polar θ , que se mide convencionalmente hacia abajo desde el poste. El área de una franja de ancho Δ θ en un dado θ en una esfera unitaria es

Δ A = 2 π pecado θ   Δ θ = 2 π porque ( π / 2 d )   Δ d = 2 π porque d   Δ d

Para traducir esto en algo que podamos usar, decimos que la probabilidad de encontrar una estrella en un pequeño rango de declinaciones

d PAG porque d   d d ,
entonces la probabilidad acumulada de encontrar un valor de d entre 90 y d es
PAG ( d ) = 90 d porque d   d d 90 + 90 porque d   d d ,
donde el denominador asegura que la proporcionalidad esté correctamente normalizada. De esto, obtenemos
(1) PAG ( d ) = 1 2 ( 1 + pecado d )

El procedimiento para obtener entonces una declinación aleatoria es asignar un número aleatorio uniforme a la probabilidad acumulada PAG entre 0 y 1. Entonces de la ecuación (1) tenemos

pecado d = 2 PAG 1
d = pecado 1 ( 2 PAG 1 ) ,
dónde PAG es un número aleatorio entre 0 y 1.

Creo que quiere decir que el área cubierta en el ángulo delta cos(delta)no es sin(delta), ya que el área (longitud) cubierta por el ecuador celeste a 0 grados es el valor más alto, no el valor más bajo.
@barrycarter Ahora resuelto - ver editar. Mi declaración original era correcta.
Acabo de hacer una versión Sage / Numpy que puedes rotar, desplazar y hacer zoom. Publicaré el enlace en el siguiente comentario. (Lamento que el código sea un poco críptico, tuve que recortarlo para hacerlo lo suficientemente pequeño como para caber).
@ProfRob ¡Bravo! Gracias por tomarse el tiempo para resolver esto tan explícitamente. Las coordenadas esféricas tienden a comenzar con θ = 0 en la parte superior mientras que la latitud y la declinación lo colocan en el medio, así que siempre me confundo y empiezo a intentarlo pecado 1 y porque 1 hasta que algo funcione.

Aquí hay más pitones de los que puedes sacudir con un telescopio. Acabo de usar el algoritmo de @ProfRob . Esta es solo una secuencia de comandos de Python, la respuesta real a la pregunta es la respuesta de @ProfRob y la acabo de escribir. Las matemáticas detrás de la generación de distribuciones estadísticamente uniformes también se explican muy bien allí.

Python está debajo de las parcelas. Puede ver en un gráfico XY que se adelgazan en la parte superior e inferior.

La trama 3D está en vivo, puede usar el cursor y mover la esfera. Por supuesto, no AQUÍ :-), sino en su propia ventana de Python si ejecuta el script.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

import numpy as np
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D

halfpi, pi, twopi = [f*np.pi for f in [0.5, 1, 2]]

degs, rads = 180/pi, pi/180

# do radians first, then convert later

nstars = 2000

ran1, ran2 = np.random.random(2*nstars).reshape(2, -1)

RA  = twopi * (ran1 - 0.5)
dec = np.arcsin(2.*(ran2-0.5)) # Hey Barry Carter!

funcs = [np.cos, np.sin]

cosRA,  sinRA  = [f(RA)  for f in funcs]
cosdec, sindec = [f(dec) for f in funcs]

x = cosRA * cosdec
y = sinRA * cosdec
z = sindec

decs = (np.arange(11)-5) * halfpi / 6.
RAs  = (np.arange(12)-5) * halfpi / 6.

theta = np.linspace(0, twopi, 101)

costh, sinth = [f(theta) for f in funcs]
zerth = np.zeros_like(theta)

fig = plt.figure()

ax  = fig.add_subplot(1, 1, 1, projection='3d')

ax.plot(x, y, z, '.k')

# lines of declination
xvals = costh * np.cos(decs)[:, None]
yvals = sinth * np.cos(decs)[:, None]
zvals = zerth + np.sin(decs)[:, None]
for x, y, z in zip(xvals, yvals, zvals):
    plt.plot(x, y, z, '-g', linewidth=0.8)

# lines of Right Ascention
xvals = costh * np.cos(RAs)[:, None]
yvals = costh * np.sin(RAs)[:, None]
zvals = sinth + np.zeros_like(RAs)[:, None]
for x, y, z in zip(xvals, yvals, zvals):
    plt.plot(x, y, z, '-r', linewidth=0.8)

ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_zlim(-1.1, 1.1)

ax.view_init(elev=30, azim=15)

plt.show()

if True:
    plt.figure()
    plt.plot(degs*RA, degs*dec, '.k')
    plt.xlim(-180, 180)
    plt.ylim(-90, 90)
    plt.show()
Muchas gracias, ¿estás seguro de que dec = np.arcsin(2.*(ran2-0.5)) ? Parece ser bueno, pero no entiendo muy bien por qué arcsin y no arcos, por ejemplo.
¡Muy lindo! Pero cuando trato de rotar, solo se mueve un poco en la dirección de mi movimiento, después de lo cual tengo que hacer clic/arrastrar nuevamente para otro pequeño movimiento. ¿No se supone que debes poder arrastrar la trama sin problemas? ¿Se trata sólo de mí?
Bien, lo entiendo: cuando integras el coseno, obtienes -sin, por lo que aparentemente arcsen es el correcto para usar.
@pela funciona muy bien en mi instalación IDLE que me encanta, pero la respuesta de trama 3D en mi instalación de anaconda no funciona del todo bien. ¡Esta sería una buena pregunta para stackoverflow! Simplemente edite el script hasta el ejemplo mínimo, completo y verificable antes de publicarlo. También puede vincular aquí, pero asegúrese de deshacerse de la mayor cantidad posible.
@uhoh: Interesante, también tengo instalado Anaconda. Buena idea con Stackoverflow, ¡gracias!

Respuesta complementaria para futuros lectores aquí que estén más interesados ​​​​en la "uniformidad" que en la "aleatoriedad" o el "realismo" de la distribución.

Aquí hay dos respuestas de desbordamiento de pila que son similares, pero el desplazamiento en el método del girasol se puede modificar para optimizar la "uniformidad" de la distribución casi uniforme.

La razón principal por la que los patrones se ven diferentes es que los "polos" donde comienza y termina la espiral están en el costado de la "esfera_fibonacci" y en la parte superior para el "girasol_en_esfera".

dos distribuciones casi uniformes de puntos en una esfera

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def sunflower_on_sphere(n=1000):
    # https://stackoverflow.com/a/44164075/3904031

    indices = np.arange(n) + 0.5

    phi = np.arccos(1 - 2 * indices / n)
    theta = np.pi * (1 + np.sqrt(5)) * indices

    x, y, z = np.cos(theta) * np.sin(phi), np.sin(theta) * np.sin(phi), np.cos(phi);

    return np.vstack([x, y, z])

def fibonacci_sphere(n=1000):
    # https://stackoverflow.com/a/26127012/3904031
    
    phi = np.pi * (3. - np.sqrt(5.))  # golden angle in radians

    theta = phi * np.arange(n)  # golden angle increment

    y = np.linspace(1, -1, n)  # y goes from 1 to -1
    
    r = np.sqrt(1 - y**2)  # radius at y
    
    x, z = [r*f(theta) for f in (np.cos, np.sin)]

    points = np.vstack([x, y, z])

    return points

n = 2000

xf, yf, zf = fibonacci_sphere(n)
xs, ys, zs = sunflower_on_sphere(n)

fig = plt.figure(figsize=[14, 7.5])

axf  = fig.add_subplot(1, 2, 1, projection='3d', proj_type = 'ortho')
axs  = fig.add_subplot(1, 2, 2, projection='3d', proj_type = 'ortho')

axf.scatter(xf, yf, zf, marker='.', c=yf, cmap='gray')
axs.scatter(xs, ys, zs, marker='.', c=xs, cmap='gray')

for ax in (axf, axs):
    ax.set_xlim(-1.1, 1.1)
    ax.set_ylim(-1.1, 1.1)
    ax.set_zlim(-1.1, 1.1)
    ax.set_box_aspect([1,1,1])
    ax.set_box_aspect([1,1,1]) # https://stackoverflow.com/a/68242226/3904031

axf.view_init(6, -60)
axs.view_init(6, -150)
axf.set_title('fibonacci_sphere(n='+str(n)+')')
axs.set_title('sunflower_on_sphere(n='+str(n)+')')
plt.show()

Esto es más ciencia informática o matemáticas que astronomía, pero aquí hay una función random_point_on_unit_sphere()que crea puntos aleatorios en una esfera 3D unitaria, que luego es utilizada por otra función random_point_on_sky()para convertir a valores RA y dec, usando astropy . Finalmente, la función print_random_star_coords()imprime un número de pares RA,dec.

import numpy as np
from astropy.coordinates import SkyCoord
from astropy import units as u

def random_point_on_unit_sphere():
    while True:
        R   = np.random.rand(3) #Random point in box
        R   = 2*R - 1
        rsq = sum(R**2)
        if rsq < 1: break       #Use r only if |r|<1 in order not to favor corners of box
    return R / np.sqrt(rsq)     #Normalize to unit vector

def random_point_on_sky():
    p     = random_point_on_unit_sphere()
    r     = np.linalg.norm(p)
    theta = 90 - (np.arccos(p[2] / r)    / np.pi * 180)            #theta and phi values in degrees
    phi   =       np.arctan(p[1] / p[0]) / np.pi * 180
    c     = SkyCoord(ra=phi, dec=theta, unit=(u.degree, u.degree)) #Create coordinate
    return c.ra.hms, c.dec.deg                                     #Many different formats are possible, e.g c.ra.hour for decimal hour values

def print_random_star_coords(nstars):
    for n in range(nstars):
        RA,dec = random_point_on_sky()
        print(RA,dec)