Source code for pynlo.light.beam

# -*- coding: utf-8 -*-
"""
Created on Thu Jun 11 10:08:31 2015
This file is part of pyNLO.

    pyNLO is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    pyNLO is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with pyNLO.  If not, see <http://www.gnu.org/licenses/>.
    
@author: ycasg
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
from scipy import constants, optimize

[docs]class OneDBeam: """ Simple Gaussian beam class for propagation and calculating field intensities. Contains beam shape and propagation axis information. The beam waist is held independent of index of refraction, from which the confocal parameter and beam geometry can be calculated. According to Boyd, who cites Klienman (1966) and Ward and New (1969), it is generally true that the confocal parameter is conserved in harmonic generation and DFG. This parameter is b = 2 pi w0**2 / lambda.""" _w0 = 1.0 _lambda0 = None _crystal_ID = None _n_s_cache = None
[docs] def __init__(self, waist_meters = 1.0, this_pulse = None, axis = None): """ Initialize class instance. From waist, confocal parameter is derived. A Pulse class is input, and it is assumed that each color focuses to the same waist size at the same point. From this, the (chromatic) confocal parameter b(lambda) is calculated""" self._lambda0 = this_pulse.wl_mks self.axis = axis self.set_w0( waist_meters )
def calc_confocal(self, n_s = 1.0): return (2.0*np.pi) * self.waist**2 * (n_s/self._lambda0) def set_w0(self, w0): self._w0 = w0 def _get_w0(self): return self._w0 waist = property(_get_w0)
[docs] def calculate_waist(self, z, n_s = 1.0): """ Calculate the beam waist a distance z from the focus. The expression is : w(z) = w0 (1+ ( 2z/b)**2 )**1/2 """ b = self.calc_confocal(n_s) return self.waist * np.sqrt(1. + ( 2.0* z / b)**2 )
[docs] def calculate_zR(self, n_s = 1.0): """ Calculate Rayleigh range, accounting for index of refraction. """ return self.calc_confocal(n_s) / 2.0
[docs] def calculate_R(self, z, n_s = 1.0): """ Calculate beam curvature. : R(z) = z * [ 1 + (z_R/ z)**2 ]""" z_r = self.calculate_zR(n_s) return z * (1 + (z_r/z)**2)
[docs] def calculate_gouy_phase(self, z, n_s): """ Return the Gouy phase shift due to focusing a distance z in a crystal, where it is assumed that the focus is at crystal_length / 2.0. Return is exp(i psi), as in eq 37 in Siegman Ch 17.4, where A ~ exp(-ikz + i psi).""" z_r = self.calculate_zR(n_s) psi_gouy = np.arctan2(z, z_r ) return np.exp(1j*psi_gouy)
def _rtP_to_a(self, n_s, z, waist = None): """ Calculate conversion constant from electric field to average power from indices of refraction: A = P_to_a * rtP """ if waist is None: waist = self.calculate_waist(z, n_s) return 1.0 / np.sqrt( np.pi * waist**2 * n_s * \ constants.epsilon_0 * constants.speed_of_light)
[docs] def rtP_to_a(self, n_s, z = None): """ Calculate conversion constant from electric field to average power from pulse and crystal class instances: A ** 2 = rtP_to_a**2 * P """ return self._rtP_to_a(n_s, z, self.waist)
[docs] def rtP_to_a_2(self, pulse_instance, crystal_instance, z = None, waist = None): """ Calculate conversion constant from electric field to average power from pulse and crystal class instances: A ** 2 = rtP_to_a**2 * P """ n_s = self.get_n_in_crystal(pulse_instance, crystal_instance) return self._rtP_to_a(n_s, z, waist)
[docs] def calc_overlap_integral(self, z, this_pulse, othr_pulse, othr_beam,\ crystal_instance, reverse_order = False): """ Calculate overlap integral (field-square) between this beam and Beam instance second_beam inside of a crystal. If reverse_order is true, then the order of second_beam will be reversed. """ n1 = self.get_n_in_crystal(this_pulse, crystal_instance) n2 = othr_beam.get_n_in_crystal(othr_pulse, crystal_instance) zr1 = self.calculate_zR(n1) zr2 = othr_beam.calculate_zR(n2) k1 = self.get_k_in_crystal(this_pulse, crystal_instance) k2 = othr_beam.get_k_in_crystal(othr_pulse, crystal_instance) # This expression only accounts for beam size #return (2*w2*w2/(w1**2+w2**2))**2 # Expression below accounts for curvature: return (4*k1*k2*zr1*zr2)/(k2**2*(z**2 + zr1**2) - 2*k1*k2*(z**2 - zr1*zr2) + k1**2*(z**2 + zr2**2))
[docs] def set_waist_to_match_confocal(self, this_pulse, othr_pulse, othr_beam,\ crystal_instance): """ Calculate waist w0 for a beam match confocal parameters with othr_beam """ n1 = self.get_n_in_crystal(this_pulse, crystal_instance) n2 = othr_beam.get_n_in_crystal(othr_pulse, crystal_instance) zr = othr_beam.calculate_zR(n2) w0 = np.sqrt( 2.0*zr*self._lambda0/(2.0*np.pi*n1)) self.set_w0(w0)
[docs] def set_waist_to_match_central_waist(self, this_pulse,w0_center,crystal_instance): """ Calculate waist w0 for a beam match so that all confocal parameters are equal while matching waist w0_center at center color of this beam """ n1 = self.get_n_in_crystal(this_pulse, crystal_instance) zr = (np.pi) * w0_center**2 * (n1[len(n1)>>1]/self._lambda0[len(self._lambda0)>>1]) w0 = np.sqrt( 2*zr*self._lambda0/(2.0*np.pi*n1)) self.set_w0(w0)
[docs] def calc_optimal_beam_overlap_in_crystal(self, this_pulse, othr_pulse, othr_beam,\ crystal_instance, L = None): """ Calculate waist w0 for a beam to maximuze the integral (field-square) between it beam and Beam instance second_beam integrated along the length of a crystal. If L is not specified, then the crystal length is used. """ if L is None: L = crystal_instance.length_mks n1 = self.get_n_in_crystal(this_pulse, crystal_instance) n2 = othr_beam.get_n_in_crystal(othr_pulse, crystal_instance) zr2 = othr_beam.calculate_zR(n2) k1 = self.get_k_in_crystal(this_pulse, crystal_instance) k2 = othr_beam.get_k_in_crystal(othr_pulse, crystal_instance) obj_fn = lambda zr1: -1.0*np.sum((4*k1*k2*zr1*abs(zr2) *\ np.arctan( ((k1 - k2)*L)/(k2*zr1 + k1*abs(zr2)))/\ ((k1 - k2)*(k2*zr1 + k1*abs(zr2))))) result = optimize.minimize(obj_fn, zr2, method = 'Powell') # From w0**2 = b lambda/ 2 pi n: w0_out = np.sqrt( 2.0 *result.x*self._lambda0/(2.0*np.pi*n1)) return w0_out
def get_n_in_crystal(self, pulse_instance, crystal_instance): return crystal_instance.get_pulse_n(pulse_instance, self.axis) def get_k_in_crystal(self, pulse_instance, crystal_instance): return crystal_instance.get_pulse_k(pulse_instance, self.axis)