def evolve(self, points, t):
        # Given points at time 0, return points at time t.
        lats = geometry.points_to_latitudes(points/self.radius, self.axis)
        vels = physics.fluid_rotation(self.eq_angvel, self.polar_angvel, lats)
        result = np.zeros(points.shape)

        mat = geometry.rotation_matrix(-vels * t, self.axis)
        return np.dot( points, mat.transpose() )
def generate_lead_halide(halide, ion="Pb"):
    PbX = structures.Molecule([structures.Atom(ion, 0, 0, 0)])
    if type(halide) is str:
        halide = [halide, halide, halide]

    def vdw(y):
        return PERIODIC_TABLE[units.elem_s2i(y)]['vdw_r']

    for x in halide:
        v = vdw(x)
        PbX.atoms.append(structures.Atom(x, v, 0, 0.5 * v))
        R = geometry.rotation_matrix([0, 0, 1], 120, units="deg")
        PbX.rotate(R)
    return PbX
    def spot(self, theta, phase, fracarea):
        # Given spherical coords, get absolute coords
        # Convention: theta is a latitude in degrees: 0 = equator, +90 = north pole
        # Phase is in periods: 0 = transit, 0.5 = opposition, 1 = next transit

        # find axis with z projection removed
        w = self.axis[0:2] / np.sqrt(self.axis[0]**2 + self.axis[1]**2)
        
        # apply inclination
        theta *= math.pi/180.0
        theta -= math.asin(self.axis[2])

        # get location at phase 0 (latitude only)
        c, s = math.cos(theta), math.sin(theta)
        p = self.radius * np.array( [s*w[0], s*w[1], c] )

        # rotate to phase
        mat = geometry.rotation_matrix(-2.0 * math.pi * phase, self.axis)
        p = np.dot(p, mat.transpose())

        return (p, fracarea)
def generate_lead_halide_cation(halide, cation, ion="Pb", run_opt=True):
    cml_path = fpl_constants.cml_dir
    # Check if system exists
    fname = reduce_to_name(ion, halide, cation)
    if not cml_path.endswith("/"):
        cml_path += "/"

    if os.path.exists(cml_path + fname + ".cml"):
        print("Found system in cml folder, returning system")
        system = structures.Molecule(
            files.read_cml(cml_path + fname + ".cml",
                           test_charges=False,
                           allow_errors=True)[0])
        return system

    def vdw(y):
        return PERIODIC_TABLE[units.elem_s2i(y)]['vdw_r']

    # Get the PbX3 system
    PbX3 = generate_lead_halide(halide, ion=ion)
    # Get the cation from the cml file
    atoms, bonds, _, _ = files.read_cml(cml_path + cation + ".cml",
                                        test_charges=False,
                                        allow_errors=True)
    system = structures.Molecule(atoms)
    # Align along X axis
    system.atoms = geometry.align_centroid(system.atoms)[0]
    # Rotate to Z axis
    # NOTE! In case of FA, we want flat so only translate to origin instead
    # NOTE! We have exactly 3 cations we observe: Cs, MA, FA. If 2 N, then FA
    elems = [a.element for a in system.atoms]
    if elems.count("N") == 2:
        system.translate(system.get_center_of_mass())
    else:
        R = geometry.rotation_matrix([0, 1, 0], 90, units="deg")
        system.rotate(R)
    # If N and C in system, ensure N is below C (closer to Pb)
    if "N" in elems and "C" in elems:
        N_index = [i for i, a in enumerate(system.atoms)
                   if a.element == "N"][0]
        C_index = [i for i, a in enumerate(system.atoms)
                   if a.element == "C"][0]
        if system.atoms[N_index].z > system.atoms[C_index].z:
            # Flip if needed
            R = geometry.rotation_matrix([0, 1, 0], 180, units="deg")
            system.rotate(R)
    # Offset system so lowest point is at 0 in the z dir
    z_offset = min([a.z for a in system.atoms]) * -1
    system.translate([0, 0, z_offset])

    # Add to the PbX3 system with an offset of vdw(Pb)
    system.translate([0, 0, vdw(ion)])
    system.atoms += PbX3.atoms

    # Run a geometry optimization of this system
    if run_opt:
        PbXY = orca.job(fname,
                        fpl_constants.default_routes[0],
                        atoms=system.atoms,
                        extra_section=fpl_constants.extra_section,
                        queue="batch",
                        procs=2)
        PbXY.wait()
        new_pos = orca.read(fname).atoms
        for a, b in zip(system.atoms, new_pos):
            a.x, a.y, a.z = [b.x, b.y, b.z]

    # Set OPLS types
    for a in system.atoms:
        if a.element in [ion, "Cl", "Br", "I"]:
            a.type = fpl_constants.atom_types[a.element]
            a.type_index = a.type["index"]

    # Write cml file so we don't re-generate, and return system
    files.write_cml(system, bonds=bonds, name=cml_path + fname + ".cml")
    return system
 def evolve(self, points, t):
     # Given points at time 0, return points at time t.
     mat = geometry.rotation_matrix(-self.scalar_angvel * t, self.axis)
     return np.dot( points, mat.transpose() )