Ejemplo n.º 1
0
def projected_area(mesh, normal):
    """
    Get the mesh's projected area along a certain vector
    It was initially unseen in trimesh's code
    It could be replaced by trimesh.path.polygons.projected(m, normal=[1,1,0]).area now
    See : https://github.com/mikedh/trimesh/issues/898
    
    :param mesh: Mesh
    :type mesh: trimesh.Trimesh
    :param normal: Normal vector to the plane on which the mesh should be projected
    :type normal: loas.Vec
    """
    normal = normal/np.linalg.norm(normal)
    m = copy.deepcopy(mesh)
    dir_rot = normal + loas.Vec(1,0,0)
    if np.linalg.norm(dir_rot) < 1e-6:
        dir_rot = loas.Vec(0,1,0)
    m.apply_transform(trimesh.transformations.rotation_matrix(math.pi, dir_rot.line()))
    m.apply_transform(trimesh.transformations.projection_matrix((0,0,0),(1,0,0)))
    polygons = [
        shapely.geometry.Polygon(triangle[:,1:])
        for index_triangle, triangle in enumerate(m.triangles)
        if np.linalg.norm(m.face_normals[index_triangle] - np.array([1,0,0])) < 1e-6
    ]
    poly_merged = shapely.ops.unary_union(polygons)
    return poly_merged.area
Ejemplo n.º 2
0
 def axis(self):
     """
     Return the normalized rotation axis of the quaternion
     """
     res = loas.Vec(self.b, self.c, self.d)
     if np.linalg.norm(res) == 0:
         return loas.Vec(1, 0, 0)
     return res / np.linalg.norm(res)
Ejemplo n.º 3
0
def get_Q_sfc(normal):
    """
    Returns the quaternion to pass from the reference frame to
    the local surface frame where the x axis is normal to the surface
    """
    dir_rot = normal + loas.Vec(1, 0, 0)
    if np.linalg.norm(dir_rot) < 1e-6:
        dir_rot = loas.Vec(0, 1, 0)
    return loas.Quat(0, *dir_rot)
Ejemplo n.º 4
0
 def _getRandomOrigin():
     r = sat_bs_radius*math.sqrt(random.random())
     theta = 2*math.pi*random.random()
     rel_pos = loas.Vec(
         r*math.cos(theta),
         r*math.sin(theta),
         2*sat_bs_radius
     )
     return rel_pos + sat_bs_center
Ejemplo n.º 5
0
    def euler(self):
        """
        Returns euler angle of the rotation defined by the quaternion

        It is bad to use euler angles.
        """
        q0, q1, q2, q3 = self.a, self.b, self.c, self.d
        return loas.Vec(
            math.atan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2)),
            math.asin(2 * (q0 * q2 - q3 * q1)),
            math.atan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2)))
Ejemplo n.º 6
0
    def _runSingleSim(self, sat_W, sat_Q, sat_speed, sat_temp, part_density, part_mol_mass, part_temp, with_drag_coef):
        """
        Utilitary function that runs a single simultation
        runSim is a user-friendly wrapper of this simulation
        """


        part_mass = part_mol_mass/scipy.constants.N_A
        nb_part = round(self.part_per_iteration/self.nb_workers)

        args = (
            True,
            sat_speed,
            sat_Q,
            sat_W,
            sat_temp,
            nb_part,
            part_mass,
            part_temp,
            part_density
        )
        for i in range(self.nb_workers):
            self.workers_input_queue.put(args)

        torque  = loas.Vec(0,0,0)
        drag = 0
        particle_data = []
        for _ in range(self.nb_workers):
            torque_add, drag_add = self.workers_output_queue.get()
            torque += torque_add
            drag += drag_add

        if with_drag_coef:
            S_p = loas.utils.projected_area(
                self.sat_mesh,
                sat_Q.R2V(loas.Vec(0,0,1))
            )
            drag_coef = drag*2/part_density/sat_speed**2/S_p
            return drag, torque, drag_coef

        return drag, torque
Ejemplo n.º 7
0
 def model(part_speed_i, normal, sat_temp, part_mass):
     # Schamberg model
     Q_sfc = get_Q_sfc(normal)
     part_speed_r_norm = scipy.stats.maxwell.rvs(
         scale=math.sqrt(scipy.constants.k * sat_temp / part_mass))
     theta = 2 * theta_0 / math.pi * math.asin(
         2 * random.random() - 1
     ) + theta_i  #angle par rapport à la normale à la surface (donc le vecteur (1,0,0)) dans le repère de la sfc
     phi = 2 * math.pi * random.random()  #angle dans le plan (yOz)
     part_speed_r = Q_sfc.V2R(part_speed_r_norm *
                              loas.Vec(math.cos(theta),
                                       math.sin(theta) * math.cos(phi),
                                       math.sin(theta) * math.sin(phi)))
     return part_speed_r
Ejemplo n.º 8
0
def plot_all_traj():
    plt.figure()
    ax = plt.subplot(111, projection='aitoff')
    change_labels(ax)
    for p in np.linspace(0, 2 * math.pi, 4)[:-1]:
        for t in np.linspace(0, math.pi, 4)[:-1]:
            Q0 = v2q(c2v(t, p))
            traj = np.array([
                v2c(q2v(Q)) for Q in sim_traj(Q0, loas.Vec(0, 0, 0), 50, 1000)
            ])
            plt.scatter(*v2m(t, p), color='blue')
            plt.plot(*v2m(traj[:, 0], traj[:, 1]), color='red')
            print(p, t)
    plt.grid()
    plt.show()
Ejemplo n.º 9
0
def plot_all_traj_normals():
    dt = 50
    ax = plt.figure().gca(projection='3d')
    for p in np.linspace(0, 2 * math.pi, 10)[:-1]:
        for t in np.linspace(0, math.pi, 10)[:-1]:
            Q0 = v2q(c2v(t, p))
            traj = [q2v(Q) for Q in sim_traj(Q0, loas.Vec(0, 0, 0), dt, 1000)]
            normals = []
            for i in range(1, len(traj) - 1):
                normal = (traj[i + 1] - traj[i]).cross(traj[i - 1] -
                                                       traj[i]) / dt**2
                normals.append(normal.line())
            normals = np.array(normals)
            ax.plot(*np.transpose(np.array(normals)), color='red')
            #plt.plot(*np.transpose(np.array(traj)))
            print(p, t)
    ax.auto_scale_xyz(*[[np.min(normals), np.max(normals)]] * 3)
    plt.legend()
    plt.show()
Ejemplo n.º 10
0
 def model(part_speed_i, normal, sat_temp, part_mass):
     Q_sfc = get_Q_sfc(normal)  #quaternion de passage sur la surface
     if random.random() < epsilon:
         # specular reflexion
         normal_rel_speed = (
             np.transpose(normal) @ part_speed_i)[0, 0] * normal
         part_speed_r = part_speed_i - 2 * normal_rel_speed
     else:
         part_speed_r_norm = scipy.stats.maxwell.rvs(
             scale=math.sqrt(scipy.constants.k * sat_temp / part_mass))
         theta = math.asin(
             2 * random.random() - 1
         )  #angle par rapport à la normale à la surface (donc le vecteur (1,0,0)) dans le repère de la sfc
         phi = 2 * math.pi * random.random()  #angle dans le plan (yOz)
         part_speed_r = Q_sfc.V2R(part_speed_r_norm *
                                  loas.Vec(math.cos(theta),
                                           math.sin(theta) * math.cos(phi),
                                           math.sin(theta) * math.sin(phi)))
     return part_speed_r
Ejemplo n.º 11
0
def run(w):
    mesh = trimesh.load_mesh("../models/crocus/45.stl")
    mesh.apply_scale(.01)
    sat_Q = [
        loas.Quat(math.cos(angle / 2), math.sin(angle / 2), 0, 0)
        for angle in np.linspace(0, 2 * math.pi, 100)
    ]
    drag = loas.RAD(sat_mesh=mesh,
                    model=loas.models.maxwell(0.10),
                    part_per_iteration=1e5,
                    nb_workers=8)
    drag.start()
    res = drag.runSim(sat_W=loas.Vec(float(w), 0, 0),
                      sat_Q=sat_Q,
                      sat_speed=7000,
                      sat_temp=300,
                      part_density=1e-11,
                      part_mol_mass=0.016,
                      part_temp=1800,
                      with_drag_coef=True)
    drag.stop()

    with open('res_temp/' + str(w), 'w') as f:
        f.write(str(res))
Ejemplo n.º 12
0
import loas
import math

# load mesh object and resize it
mesh = trimesh.load_mesh("./models/slab.stl")
bounds = np.array(mesh.bounds)
mesh.apply_translation(
    -(bounds[0] + bounds[1]) /
    2)  # center the satellite (the mass center should be on 0,0)
mesh.apply_scale(.08)  # rescale the model

drag = loas.RAD(sat_mesh=mesh,
                model=loas.models.maxwell(0.10),
                part_per_iteration=1e4,
                nb_workers=6)
drag.start()
sat_Q = [
    loas.Quat(math.cos(angle / 2), math.sin(angle / 2), 0, 0)
    for angle in np.linspace(0, math.pi / 2, 10)
]
print(
    drag.runSim(sat_W=loas.Vec(0, 0, 0),
                sat_Q=sat_Q,
                sat_speed=7000,
                sat_temp=300,
                part_density=1e-11,
                part_mol_mass=0.016,
                part_temp=1800,
                with_drag_coef=True))
drag.stop()
Ejemplo n.º 13
0
    phis = (math.pi * (1 + 5**0.5) * indices)%(2*math.pi)
    return thetas, phis

Ts, Ps = get_grid(500)

Qs = [v2q(c2v(t,p)) for t,p in np.transpose([Ts,Ps])]
mesh = trimesh.load_mesh("../models/satellite.stl")
drag = loas.RAD(
    sat_mesh = mesh,
    model = loas.models.maxwell(0.10),
    part_per_iteration = 1e5,
    nb_workers = 8
)
drag.start()
res = drag.runSim
    sat_W = loas.Vec(0,0,0),
    sat_Q = Qs,
    sat_speed = 7000,
    sat_temp = 300,
    part_density = 1e-11,
    part_mol_mass = 0.016,
    part_temp = 1800,
    with_drag_coef = False
)
drag.stop()

# save the toque in the satellite frame bc it dont depends on the rotation around (0,0,1) in this frame
Cs = [Qs[index].R2V(torque) for index, (drag, torque) in enumerate(res)]

for name, obj in (('T.npy', Ts), ('P.npy', Ps), ('C.npy', Cs)):
    with open('res_temp/'+name, 'wb') as f:
Ejemplo n.º 14
0
def v2q(vec):  #Q.V2R(loas.Vec(x,z,y)) = (0,0,1)
    assert np.linalg.norm(vec) - 1 < 1e-10
    axis = vec + loas.Vec(0, 0, -1)
    if np.linalg.norm(axis) < 1e-6:
        axis = loas.Vec(1, 0, 0)
    return loas.Quat(0, *axis)
Ejemplo n.º 15
0
def c2v(theta, phi):
    return loas.Vec(
        math.sin(theta) * math.cos(phi),
        math.sin(theta) * math.sin(phi), math.cos(theta))
Ejemplo n.º 16
0
def q2v(Q):
    return Q.R2V(loas.Vec(0, 0, -1))
Ejemplo n.º 17
0
 def vec(self):
     """
     Returns a numpy array representation of the quaternion
     """
     return loas.Vec(self.a, self.b, self.c, self.d)
Ejemplo n.º 18
0
def _sparse_drag_worker(
    workers_input_queue,
    workers_output_queue,
    sat_mesh,
    max_part_batch,
    part_per_iteration,
    model
):
    """
    Unitary worker for Sparse Atmospheric drag computation. Computes the collision of a certain amount of random particles on the mesh, adn sends back the torque

    Every worker is launched only once. The trick is that, once started, communicate with the rest of the program through Queues.
    There are two queues, one for input, one for output. The worker waits for any input and once it gets it, runs the simulation, and outputs its parameters.

    :param workers_input_queue: Queue which is used to pass parameters to the worker
    :type workers_input_queue: multiprocessing.Queue
    :param workers_output_queue: Queue which is used to sends the workers simulation result
    :type workers_input_queue: multiprocessing.Queue
    :param create_batch_data_save: if set to true, the worker will output at each iteration a list containing the result of every particle simultation so that it can be worked into a batch and printed on the pyglet's window.
    :type create_batch_data_save: bool
    :param sat_mesh: Satellite's mesh
    :type sat_mesh: trimesh.Trimesh
    :param sat_bs_radius: Radius of the bounding sphere of the saellite, i.e. the sphere that entirely includes the satellite. It defines the radius of thje circle in witch it is needed to generate particles
    :type sat_bs_radius: float
    :param max_part_batch: Maximum number of particles given at once to the ray tester. If set to 0, disables, the limit.
    :param max_part_batch: int
    """

    sat_bs_radius = sat_mesh.bounding_sphere.extents[0]/2
    sat_bs_center = loas.Vec(*sat_mesh.bounding_sphere.center_mass)
    workers_running = True

    def _getRandomOrigin():
        r = sat_bs_radius*math.sqrt(random.random())
        theta = 2*math.pi*random.random()
        rel_pos = loas.Vec(
            r*math.cos(theta),
            r*math.sin(theta),
            2*sat_bs_radius
        )
        return rel_pos + sat_bs_center

    while workers_running:
        args = (
            workers_running,
            sat_speed,
            sat_Q,
            sat_W,
            sat_temp,
            part_pending,
            part_mass,
            part_temp,
            part_density
        ) = workers_input_queue.get()
        if not workers_running:
            return

        part_speed = loas.Vec(0,0,-sat_speed)
        dir_sat = sat_Q.R2V(part_speed)[:,0]
        torque_dt  = loas.Vec(0,0,0)
        drag_dt = 0

        while part_pending > 0:
            # disable batching if max_part_batch to 0
            if max_part_batch > 0:
                part_batch = min(part_pending, max_part_batch)
            else:
                part_batch = part_pending
            part_pending -= part_batch

            origins_sat = np.array([sat_Q.R2V(_getRandomOrigin())[:,0] for _ in range(part_batch)])
            locations, indexes_ray, indexes_tri = sat_mesh.ray.intersects_location(
                ray_origins=origins_sat,
                ray_directions=[dir_sat]*part_batch
            )

            #filter only for closest point
            locations_filtered = {}
            for index, location_sat in enumerate(locations):
                index_tri = indexes_tri[index]
                index_ray = indexes_ray[index]
                origin_sat = origins_sat[index_ray]
                dist = (origin_sat[0] - location_sat[0])**2 + (origin_sat[1] - location_sat[1])**2 + (origin_sat[2] - location_sat[2])**2

                if not index_ray in locations_filtered:
                    locations_filtered[index_ray] = (location_sat, index_tri, dist)
                elif locations_filtered[index_ray][2] > dist:
                    locations_filtered[index_ray] = (location_sat, index_tri, dist)

            # process torque given by actual hit point
            for location_sat, index_tri, _ in locations_filtered.values():
                location = sat_Q.V2R(loas.Vec(*location_sat))
                normal = sat_Q.V2R(loas.Vec(*sat_mesh.face_normals[index_tri]))
                normal /= np.linalg.norm(normal)
                part_speed_i = part_speed - sat_W.cross(location)
                part_speed_r = model(part_speed_i, normal, sat_temp, part_mass)
                momentum = part_mass*(part_speed_i-part_speed_r)
                drag_dt += ((np.transpose(part_speed)/np.linalg.norm(part_speed)) @ momentum)[0,0]
                torque_dt += location.cross(momentum)

        scale_factor = part_density / part_mass * sat_speed * math.pi*sat_bs_radius**2 / part_per_iteration
        torque = torque_dt * scale_factor
        drag = drag_dt * scale_factor

        workers_output_queue.put((torque, drag))