Ejemplo n.º 1
0
def pos_shift(
    shell_radii,
    particles_per_shell,
    res_increase_factor=85,
):
    """
    Return the relative positions shift for this shell as a numpy array.
    """
    total_particles = sum(particles_per_shell)
    relative_positions = numpy.zeros(
        total_particles * 3,
        dtype=float,
    ).reshape(total_particles, 3)
    ipos = 1

    for shell in range(1, len(shell_radii)):
        num_pts = particles_per_shell[shell]
        indices = numpy.arange(0, num_pts, dtype=float) + 0.5

        phi = arccos(1 - 2 * indices / num_pts)
        theta = pi * (1 + 5**0.5) * indices

        ipos2 = ipos + num_pts
        x = shell_radii[shell] * cos(theta) * sin(phi)
        y = shell_radii[shell] * sin(theta) * sin(phi)
        z = shell_radii[shell] * cos(phi)

        relative_positions[ipos:ipos2, 0:3] = numpy.dstack((x, y, z))
        ipos = ipos2

    return relative_positions
Ejemplo n.º 2
0
    def test31(self):
        """ 
        test trigonometric unit stuff
        """
        self.assertEqual(units.pi, numpy.pi)
        a = units.pi
        self.assertEqual(trigo.to_rad(a), numpy.pi | units.rad)
        self.assertEqual(trigo.to_deg(a), 180. | units.deg)
        self.assertEqual(trigo.to_rev(a), 0.5 | units.rev)
        a = 90 | units.deg
        self.assertEqual(trigo.to_rad(a), numpy.pi / 2 | units.rad)
        self.assertEqual(trigo.to_deg(a), 90. | units.deg)
        self.assertEqual(trigo.to_rev(a), 0.25 | units.rev)
        a = 0.75 | units.rev
        self.assertEqual(trigo.to_rad(a), 3 / 2. * numpy.pi | units.rad)
        self.assertEqual(trigo.to_deg(a), 270. | units.deg)
        self.assertEqual(trigo.to_rev(a), 0.75 | units.rev)
        a = 2 * numpy.pi
        self.assertEqual(trigo.to_rad(a), 2 * numpy.pi | units.rad)
        self.assertEqual(trigo.to_deg(a), 360. | units.deg)
        self.assertEqual(trigo.to_rev(a), 1. | units.rev)

        a = 45. | units.deg
        self.assertEqual(trigo.sin(a), numpy.sin(45. / 180 * numpy.pi))
        self.assertEqual(trigo.cos(a), numpy.cos(45. / 180 * numpy.pi))
        self.assertEqual(trigo.tan(a), numpy.tan(45. / 180 * numpy.pi))

        a = 1. | units.rad
        self.assertEqual(trigo.sin(a), numpy.sin(1.))
        self.assertEqual(trigo.cos(a), numpy.cos(1.))
        self.assertEqual(trigo.tan(a), numpy.tan(1.))

        a = 0.125 | units.rev
        self.assertEqual(trigo.sin(a), numpy.sin(45. / 180 * numpy.pi))
        self.assertEqual(trigo.cos(a), numpy.cos(45. / 180 * numpy.pi))
        self.assertEqual(trigo.tan(a), numpy.tan(45. / 180 * numpy.pi))

        a = 45. | units.deg
        self.assertAlmostEqual(trigo.arcsin(trigo.sin(a)), 45. | units.deg, 13)
        self.assertAlmostEqual(trigo.arccos(trigo.cos(a)), 45. | units.deg, 13)
        self.assertAlmostEqual(trigo.arctan(trigo.tan(a)), 45. | units.deg, 13)
Ejemplo n.º 3
0
    def test31(self):
        """ 
        test trigonometric unit stuff
        """
        self.assertEqual(units.pi,numpy.pi)
        a=units.pi
        self.assertEqual(trigo.to_rad(a), numpy.pi | units.rad)
        self.assertEqual(trigo.to_deg(a), 180. | units.deg)
        self.assertEqual(trigo.to_rev(a), 0.5 | units.rev)
        a=90 | units.deg
        self.assertEqual(trigo.to_rad(a), numpy.pi/2 | units.rad)
        self.assertEqual(trigo.to_deg(a), 90. | units.deg)
        self.assertEqual(trigo.to_rev(a), 0.25 | units.rev)
        a=0.75 | units.rev
        self.assertEqual(trigo.to_rad(a), 3/2.*numpy.pi | units.rad)
        self.assertEqual(trigo.to_deg(a), 270. | units.deg)
        self.assertEqual(trigo.to_rev(a), 0.75 | units.rev)
        a=2*numpy.pi
        self.assertEqual(trigo.to_rad(a), 2*numpy.pi | units.rad)
        self.assertEqual(trigo.to_deg(a), 360. | units.deg)
        self.assertEqual(trigo.to_rev(a), 1. | units.rev)

        a=45. | units.deg
        self.assertEqual(trigo.sin(a),numpy.sin(45./180*numpy.pi))
        self.assertEqual(trigo.cos(a),numpy.cos(45./180*numpy.pi))
        self.assertEqual(trigo.tan(a),numpy.tan(45./180*numpy.pi))

        a=1. | units.rad
        self.assertEqual(trigo.sin(a),numpy.sin(1.))
        self.assertEqual(trigo.cos(a),numpy.cos(1.))
        self.assertEqual(trigo.tan(a),numpy.tan(1.))

        a=0.125 | units.rev
        self.assertEqual(trigo.sin(a),numpy.sin(45./180*numpy.pi))
        self.assertEqual(trigo.cos(a),numpy.cos(45./180*numpy.pi))
        self.assertEqual(trigo.tan(a),numpy.tan(45./180*numpy.pi))

        a=45. | units.deg
        self.assertAlmostEqual(trigo.arcsin(trigo.sin(a)),45. | units.deg,13)
        self.assertAlmostEqual(trigo.arccos(trigo.cos(a)),45. | units.deg,13)
        self.assertAlmostEqual(trigo.arctan(trigo.tan(a)),45. | units.deg,13)
Ejemplo n.º 4
0
def get_orbital_elements_from_arrays(rel_position_raw,
                                     rel_velocity_raw,
                                     total_masses,
                                     G=nbody_system.G):
    """
    Orbital elements from array of relative positions and velocities vectors,
    based on orbital_elements_from_binary and adapted to work for arrays (each
    line characterises a two body problem).

    For circular orbits (eccentricity=0): returns argument of pericenter = 0.,
        true anomaly = 0.

    For equatorial orbits (inclination=0): longitude of ascending node = 0,
        argument of pericenter = arctan2(e_y,e_x).

    :argument rel_position: array of vectors of relative positions of the
    two-body systems
    :argument rel_velocity: array of vectors of relative velocities of the
    two-body systems
    :argument total_masses: array of total masses for two-body systems
    :argument G: gravitational constant

    :output semimajor_axis: array of semi-major axes
    :output eccentricity: array of eccentricities
    :output period: array of orbital periods
    :output inc: array of inclinations [radians]
    :output long_asc_node: array of longitude of ascending nodes [radians]
    :output arg_per_mat: array of argument of pericenters [radians]
    :output true_anomaly: array of true anomalies [radians]
    """
    if len(numpy.shape(rel_position_raw)) == 1:
        rel_position = numpy.zeros([1, 3]) * rel_position_raw[0]
        rel_position[0, 0] = rel_position_raw[0]
        rel_position[0, 1] = rel_position_raw[1]
        rel_position[0, 2] = rel_position_raw[2]
        rel_velocity = numpy.zeros([1, 3]) * rel_velocity_raw[0]
        rel_velocity[0, 0] = rel_velocity_raw[0]
        rel_velocity[0, 1] = rel_velocity_raw[1]
        rel_velocity[0, 2] = rel_velocity_raw[2]
    else:
        rel_position = rel_position_raw
        rel_velocity = rel_velocity_raw

    separation = (rel_position**2).sum(axis=1)**0.5
    n_vec = len(rel_position)

    speed_squared = (rel_velocity**2).sum(axis=1)

    semimajor_axis = (G * total_masses * separation /
                      (2. * G * total_masses - separation * speed_squared))

    neg_ecc_arg = (
        (to_quantity(rel_position).cross(rel_velocity)**2).sum(axis=-1) /
        (G * total_masses * semimajor_axis))
    filter_ecc0 = (1. <= neg_ecc_arg)
    eccentricity = numpy.zeros(separation.shape)
    eccentricity[~filter_ecc0] = numpy.sqrt(1.0 - neg_ecc_arg[~filter_ecc0])
    eccentricity[filter_ecc0] = 0.

    # angular momentum
    mom = to_quantity(rel_position).cross(rel_velocity)

    # inclination
    inc = arccos(mom[:, 2] / to_quantity(mom).lengths())

    # Longitude of ascending nodes, with reference direction along x-axis
    asc_node_matrix_unit = numpy.zeros(rel_position.shape)
    z_vectors = numpy.zeros([n_vec, 3])
    z_vectors[:, 2] = 1.
    z_vectors = z_vectors | units.none
    ascending_node_vectors = z_vectors.cross(mom)
    filter_non0_incl = (to_quantity(ascending_node_vectors).lengths().number >
                        0.)
    asc_node_matrix_unit[~filter_non0_incl] = numpy.array([1., 0., 0.])
    an_vectors_len = to_quantity(
        ascending_node_vectors[filter_non0_incl]).lengths()
    asc_node_matrix_unit[filter_non0_incl] = normalize_vector(
        ascending_node_vectors[filter_non0_incl], an_vectors_len)
    long_asc_node = arctan2(asc_node_matrix_unit[:, 1],
                            asc_node_matrix_unit[:, 0])

    # Argument of periapsis using eccentricity a.k.a. Laplace-Runge-Lenz vector
    mu = G * total_masses
    pos_unit_vecs = normalize_vector(rel_position, separation)
    mom_len = to_quantity(mom).lengths()
    mom_unit_vecs = normalize_vector(mom, mom_len)
    e_vecs = (normalize_vector(to_quantity(rel_velocity).cross(mom), mu) -
              pos_unit_vecs)

    # Argument of pericenter cannot be determined for e = 0,
    # in this case return 0.0 and 1.0 for the cosines
    e_vecs_norm = (e_vecs**2).sum(axis=1)**0.5
    filter_non0_ecc = (e_vecs_norm > 1.e-15)
    arg_per_mat = VectorQuantity(array=numpy.zeros(long_asc_node.shape),
                                 unit=units.rad)
    cos_arg_per = numpy.zeros(long_asc_node.shape)
    arg_per_mat[~filter_non0_ecc] = 0. | units.rad
    cos_arg_per[~filter_non0_ecc] = 1.

    e_vecs_unit = numpy.zeros(rel_position.shape)
    e_vecs_unit[filter_non0_ecc] = normalize_vector(
        e_vecs[filter_non0_ecc], e_vecs_norm[filter_non0_ecc])
    cos_arg_per[filter_non0_ecc] = (e_vecs_unit[filter_non0_ecc] *
                                    asc_node_matrix_unit[filter_non0_ecc]).sum(
                                        axis=-1)
    e_cross_an = numpy.zeros(e_vecs_unit.shape)
    e_cross_an[filter_non0_ecc] = numpy.cross(
        e_vecs_unit[filter_non0_ecc], asc_node_matrix_unit[filter_non0_ecc])
    e_cross_an_norm = (e_cross_an**2).sum(axis=1)**0.5
    filter_non0_e_cross_an = (e_cross_an_norm != 0.)
    ss = -numpy.sign((mom_unit_vecs[filter_non0_e_cross_an] *
                      e_cross_an[filter_non0_e_cross_an]).sum(axis=-1))
    # note change in size in sin_arg_per and cos_arg_per; they are not used further
    sin_arg_per = ss * e_cross_an_norm[filter_non0_e_cross_an]
    cos_arg_per = cos_arg_per[filter_non0_e_cross_an]
    arg_per_mat[filter_non0_e_cross_an] = arctan2(sin_arg_per, cos_arg_per)

    # in case longitude of ascending node is 0, omega=arctan2(e_y,e_x)
    arg_per_mat[~filter_non0_e_cross_an & filter_non0_ecc] = (arctan2(
        e_vecs[~filter_non0_e_cross_an & filter_non0_ecc, 1],
        e_vecs[~filter_non0_e_cross_an & filter_non0_ecc, 0]))
    filter_negative_zmom = (~filter_non0_e_cross_an
                            & filter_non0_ecc
                            & (mom[:, 2] < 0. * mom[0, 0]))
    arg_per_mat[filter_negative_zmom] = (2. * numpy.pi -
                                         arg_per_mat[filter_negative_zmom])

    # true anomaly
    cos_true_anomaly = (e_vecs_unit * pos_unit_vecs).sum(axis=-1)
    e_cross_pos = numpy.cross(e_vecs_unit, pos_unit_vecs)
    ss2 = numpy.sign((mom_unit_vecs * e_cross_pos).sum(axis=-1))
    sin_true_anomaly = ss2 * (e_cross_pos**2).sum(axis=1)**0.5
    true_anomaly = arctan2(sin_true_anomaly, cos_true_anomaly)

    return (semimajor_axis, eccentricity, true_anomaly, inc, long_asc_node,
            arg_per_mat)
Ejemplo n.º 5
0
def get_orbital_elements_from_arrays(
        rel_position_raw, rel_velocity_raw,
        total_masses, G=nbody_system.G):
    """
    Orbital elements from array of relative positions and velocities vectors,
    based on orbital_elements_from_binary and adapted to work for arrays (each
    line characterises a two body problem).

    For circular orbits (eccentricity=0): returns argument of pericenter = 0.,
        true anomaly = 0.

    For equatorial orbits (inclination=0): longitude of ascending node = 0,
        argument of pericenter = arctan2(e_y,e_x).

    :argument rel_position: array of vectors of relative positions of the
    two-body systems
    :argument rel_velocity: array of vectors of relative velocities of the
    two-body systems
    :argument total_masses: array of total masses for two-body systems
    :argument G: gravitational constant

    :output semimajor_axis: array of semi-major axes
    :output eccentricity: array of eccentricities
    :output period: array of orbital periods
    :output inc: array of inclinations [radians]
    :output long_asc_node: array of longitude of ascending nodes [radians]
    :output arg_per_mat: array of argument of pericenters [radians]
    :output true_anomaly: array of true anomalies [radians]
    """
    if len(numpy.shape(rel_position_raw)) == 1:
        rel_position = numpy.zeros([1, 3]) * rel_position_raw[0]
        rel_position[0, 0] = rel_position_raw[0]
        rel_position[0, 1] = rel_position_raw[1]
        rel_position[0, 2] = rel_position_raw[2]
        rel_velocity = numpy.zeros([1, 3]) * rel_velocity_raw[0]
        rel_velocity[0, 0] = rel_velocity_raw[0]
        rel_velocity[0, 1] = rel_velocity_raw[1]
        rel_velocity[0, 2] = rel_velocity_raw[2]
    else:
        rel_position = rel_position_raw
        rel_velocity = rel_velocity_raw

    separation = (rel_position**2).sum(axis=1)**0.5
    n_vec = len(rel_position)

    speed_squared = (rel_velocity**2).sum(axis=1)

    semimajor_axis = (
            G * total_masses * separation
            / (2. * G * total_masses - separation * speed_squared)
            )

    neg_ecc_arg = (
            (
                to_quantity(rel_position).cross(rel_velocity)**2
                ).sum(axis=-1)
            / (G * total_masses * semimajor_axis)
            )
    filter_ecc0 = (1. <= neg_ecc_arg)
    eccentricity = numpy.zeros(separation.shape)
    eccentricity[~filter_ecc0] = numpy.sqrt(1.0 - neg_ecc_arg[~filter_ecc0])
    eccentricity[filter_ecc0] = 0.

    # angular momentum
    mom = to_quantity(rel_position).cross(rel_velocity)

    # inclination
    inc = arccos(mom[:, 2]/to_quantity(mom).lengths())

    # Longitude of ascending nodes, with reference direction along x-axis
    asc_node_matrix_unit = numpy.zeros(rel_position.shape)
    z_vectors = numpy.zeros([n_vec, 3])
    z_vectors[:, 2] = 1.
    z_vectors = z_vectors | units.none
    ascending_node_vectors = z_vectors.cross(mom)
    filter_non0_incl = (
            to_quantity(ascending_node_vectors).lengths().number > 0.)
    asc_node_matrix_unit[~filter_non0_incl] = numpy.array([1., 0., 0.])
    an_vectors_len = to_quantity(
            ascending_node_vectors[filter_non0_incl]).lengths()
    asc_node_matrix_unit[filter_non0_incl] = normalize_vector(
            ascending_node_vectors[filter_non0_incl],
            an_vectors_len)
    long_asc_node = arctan2(
            asc_node_matrix_unit[:, 1],
            asc_node_matrix_unit[:, 0])

    # Argument of periapsis using eccentricity a.k.a. Laplace-Runge-Lenz vector
    mu = G*total_masses
    pos_unit_vecs = normalize_vector(rel_position, separation)
    mom_len = to_quantity(mom).lengths()
    mom_unit_vecs = normalize_vector(mom, mom_len)
    e_vecs = (
            normalize_vector(
                to_quantity(rel_velocity).cross(mom), mu)
            - pos_unit_vecs
            )

    # Argument of pericenter cannot be determined for e = 0,
    # in this case return 0.0 and 1.0 for the cosines
    e_vecs_norm = (e_vecs**2).sum(axis=1)**0.5
    filter_non0_ecc = (e_vecs_norm > 1.e-15)
    arg_per_mat = VectorQuantity(
            array=numpy.zeros(long_asc_node.shape),
            unit=units.rad)
    cos_arg_per = numpy.zeros(long_asc_node.shape)
    arg_per_mat[~filter_non0_ecc] = 0. | units.rad
    cos_arg_per[~filter_non0_ecc] = 1.

    e_vecs_unit = numpy.zeros(rel_position.shape)
    e_vecs_unit[filter_non0_ecc] = normalize_vector(
            e_vecs[filter_non0_ecc],
            e_vecs_norm[filter_non0_ecc]
            )
    cos_arg_per[filter_non0_ecc] = (
            e_vecs_unit[filter_non0_ecc]
            * asc_node_matrix_unit[filter_non0_ecc]
            ).sum(axis=-1)
    e_cross_an = numpy.zeros(e_vecs_unit.shape)
    e_cross_an[filter_non0_ecc] = numpy.cross(
            e_vecs_unit[filter_non0_ecc],
            asc_node_matrix_unit[filter_non0_ecc]
            )
    e_cross_an_norm = (e_cross_an**2).sum(axis=1)**0.5
    filter_non0_e_cross_an = (e_cross_an_norm != 0.)
    ss = -numpy.sign(
            (
                mom_unit_vecs[filter_non0_e_cross_an]
                * e_cross_an[filter_non0_e_cross_an]
                ).sum(axis=-1)
            )
    # note change in size in sin_arg_per and cos_arg_per; they are not used further        
    sin_arg_per = ss*e_cross_an_norm[filter_non0_e_cross_an]
    cos_arg_per = cos_arg_per[filter_non0_e_cross_an]
    arg_per_mat[filter_non0_e_cross_an] = arctan2(sin_arg_per, cos_arg_per)

    # in case longitude of ascending node is 0, omega=arctan2(e_y,e_x)
    arg_per_mat[~filter_non0_e_cross_an & filter_non0_ecc] = (
            arctan2(
                e_vecs[~filter_non0_e_cross_an & filter_non0_ecc, 1],
                e_vecs[~filter_non0_e_cross_an & filter_non0_ecc, 0]
                )
            )
    filter_negative_zmom = (
            ~filter_non0_e_cross_an
            & filter_non0_ecc
            & (mom[:, 2] < 0.*mom[0, 0])
            )
    arg_per_mat[filter_negative_zmom] = (
            2. * numpy.pi
            - arg_per_mat[filter_negative_zmom]
            )

    # true anomaly
    cos_true_anomaly = (e_vecs_unit*pos_unit_vecs).sum(axis=-1)
    e_cross_pos = numpy.cross(e_vecs_unit, pos_unit_vecs)
    ss2 = numpy.sign((mom_unit_vecs*e_cross_pos).sum(axis=-1))
    sin_true_anomaly = ss2*(e_cross_pos**2).sum(axis=1)**0.5
    true_anomaly = arctan2(sin_true_anomaly, cos_true_anomaly)

    return (
            semimajor_axis, eccentricity, true_anomaly,
            inc, long_asc_node, arg_per_mat
            )