Source code for phienics.source

"""
.. module source

This module contains the definition of several source profiles.

"""

# Copyright (C) Daniela Saadeh 2020
# This file is part of phi-enics


from dolfin import Expression, assemble, dx
from scipy.integrate import quad

import scipy.optimize as sopt
import dolfin as d
import numpy as np

import os
import glob


[docs]class Source(object): r""" Base class for (static, spherically symmetric) sources. *Arguments* fem a :class:`fem.Fem` instance Ms source mass (in units :math:`M_P`) Rs source radius (in units :math:`{M_P}^{-1}`) f (`float` or `None`) mass fraction to be enclosed within the source radius. For `f=None`, the characteristic scale of the source profile is used from\_lut use a :math:`f(\bar{t})` look-up table to obtain an initial guess for :math:`\bar{t}`, the radial rescaling to apply so that a mass fraction `f` is enclosed within the source radius lut\_dir path of the look-up table lut\_root root of the file names for the look-up table lut\_tmin if generating a look-up table, minimum :math:`\bar{t}` to use in the computation lut\_tmax if generating a look-up table, maximum :math:`\bar{t}` to use in the computation lut\_tnum if generating a look-up table, number of :math:`\bar{t}` (between `lut\_tmin` and `lut\_tmax`) for which `f` is computed """ def __init__( self, fem, Ms=1e20, Rs=1e47, f=0.95, from_lut=True, lut_dir='./lut/', lut_root='', lut_tmin=0.8, lut_tmax=1.2, lut_tnum=100 ): self.fem = fem self.Ms = Ms self.Rs = Rs # fraction of mass within rs self.f = f # initial guess for t self.t0 = None self.t = None self.lut = None self.t_array = None self.from_lut = from_lut self.lut_dir = lut_dir self.lut_root = lut_root self.lut_tmin = lut_tmin self.lut_tmax = lut_tmax self.lut_tnum = lut_tnum # fenics Expression with the definition of the source profile, in the code rescaled units self.rho = None def set_t0( self ): # get initial guess for t (units source radius) if self.from_lut: # in the look-up table, find closest value to user-set f, for the chosen w idx_t = np.where( self.lut < self.f )[0][0] # take average of two closest t values self.t0 = 0.5 * ( self.t_array[idx_t] + self.t_array[idx_t - 1] ) else: self.t0 = 1. def dM_dr( self, r, t ): # dimensionless radial density pass def frac_mass_within_rs( self, t ): # fractional mass within rs, as a function of t return quad( self.dM_dr, 0., self.fem.mesh.rs, t )[0] def find_t( self ): # find t to obtain user-set fractional mass within rs if self.f is None: self.t = 1. else: self.set_t0() F = lambda t : self.frac_mass_within_rs(t) - self.f try: self.t = sopt.broyden1( F, self.t0 ).item() except sopt.nonlin.NoConvergence: # if the Broyden method fails, print warning and set f=None and t=1 print('****************************************************************************') print(' WARNING: could not set f=%.3e. Setting f=None and t=1.' % self.f ) print('****************************************************************************') self.f=None self.t=1. def set_lut( self ): # load or generate lut, if not present try: self.t_array = np.load( self.lut_dir + self.lut_root + 't_array.npy' ) self.lut = np.load( self.lut_dir + self.lut_root + 'lut.npy' ) except IOError: self.generate_lut() def generate_lut( self ): # generate look-up-table with the fractional mass within rs for different values of t print('Generating look-up table, this may take some time...') # create dir for the look-up table, if it doesn't exist already if not os.path.isdir(self.lut_dir): os.mkdir(self.lut_dir) # fill arrays for t and look-up table rs = self.fem.mesh.rs t_array = np.linspace( self.lut_tmin*rs, self.lut_tmax*rs, self.lut_tnum ) lut = np.zeros_like( t_array ) for j, t in enumerate(t_array): lut[j] = self.frac_mass_within_rs(t) # save for future use np.save( self.lut_dir + self.lut_root + 't_array.npy', t_array ) np.save( self.lut_dir + self.lut_root + 'lut.npy',lut) # store for current use self.t_array = t_array self.lut = lut def clean_up( self ): # dump look-up arrays once the source is built if self.lut is not None: del self.lut del self.t_array def erase_lut( self ): for filename in glob.glob( self.lut_dir + self.lut_root + '*' ): os.remove( filename )