2

I have a fairly long code that processes spectra, and along the way I need an interpolation of some points. I used to have all this code written line-by-line without any functions, and it all worked properly, but now I'm converting it to two large functions so that I can call it on other models more easily in the future. Below is my code (I have more code after the last line here that plots some things, but that's not relevant to my issue, since I've tested this with a bunch of print lines and learned that my issue arises when I call the interpolation function inside my process function.

import re
import numpy as np
import scipy.interpolate

# Required files and lists
filename = 'bpass_spectra.txt' # number of columns = 4
extinctionfile = 'ExtinctionLawPoints.txt' # R_V = 4.0
datalist = []
if filename == 'bpass_spectra.txt':
    filetype = 4
else:
    filetype = 1
if extinctionfile == 'ExtinctionLawPoints.txt':
    R_V = 4.0
else:
    R_V = 1.0 #to be determined

# Constants
h = 4.1357e-15 # Planck's constant [eV s]
c = float(3e8) # speed of light [m/s]

# Inputs
beta = 2.0 # power used in extinction law
R = 1.0 # star formation rate [Msun/yr]
z = 1.0 # redshift
M_gas = 1.0 # mass of gas
M_halo = 2e41 # mass of dark matter halo

# Read spectra file
f = open(filename, 'r')
rawlines = f.readlines()
met = re.findall('Z\s=\s(\d*\.\d+)', rawlines[0])
del rawlines[0]
for i in range(len(rawlines)):
    newlist = rawlines[i].split(' ')
    datalist.append(newlist)

# Read extinction curve data file
rawpoints = open(extinctionfile, 'r').readlines()
def interpolate(R_V, rawpoints, Elist, i):
    pointslist = []
    if R_V == 4.0:
        for i in range(len(rawpoints)):
            newlst = re.split('(?!\S)\s(?=\S)|(?!\S)\s+(?=\S)', rawpoints[i])
            pointslist.append(newlst)
        pointslist = pointslist[3:]
        lambdalist = [float(item[0]) for item in pointslist]
        k_abslist = [float(item[4]) for item in pointslist]
        xvallist = [(c*h)/(lamb*1e-6) for lamb in lambdalist]
        k_interp = scipy.interpolate.interp1d(xvallist, k_abslist)
        return k_interp(Elist[i])

# Processing function
def process(interpolate, filetype, datalist, beta, R, z, M_gas, M_halo, met):
    speclist = []
    if filetype == 4:
        metallicity = float(met[0])
        Elist = [float(item[0]) for item in datalist]
        speclambdalist = [h*c*1e9/E for E in Elist]
        met1list = [float(item[1]) for item in datalist]
        speclist.extend(met1list)
        klist, Tlist = [None]*len(speclist), [None]*len(speclist)
        if metallicity > 0.0052:
            DGRlist = [50.0*np.exp(-2.21)*metallicity]*len(speclist) # dust to gas ratio
        elif metallicity <= 0.0052:
            DGRlist = [((50.0*metallicity)**3.15)*np.exp(-0.96)]*len(speclist)
        for i in range(len(speclist)):
            if Elist[i] <= 4.1357e-3: # frequencies <= 10^12 Hz
                klist[i] = 0.1*(float(Elist[i])/(1000.0*h))**beta # extinction law [cm^2/g]
            elif Elist[i] > 4.1357e-3: # frequencies > 10^12 Hz
                klist[i] = interpolate(R_V, rawpoints, Elist, i) # interpolated function's value at Elist[i]
        print "KLIST (INTERPOLATION) ELEMENTS 0 AND 1000:", klist[0], klist[1000]
        return

The output from the print line is KLIST (INTERPOLATION) ELEMENTS 0 AND 1000: 52167.31734159269 52167.31734159269.

When I run my old code without functions, I print klist[0] and klist[1000] like I do here and get different values for each. In this new code, I get back two values that are the same from this line. This shouldn't be the case, so it must not be interpolating correctly inside my function (maybe it's not performing it on each point correctly in the loop?). Does anyone have any insight? It would be unreasonable to post my entire code with all the used text files here (they're very large), so I'm not expecting anyone to run it, but rather examine how I use and call my functions.

Edit: Below is the original version of my code up to the interpolation point without the functions (which works).

import re
import numpy as np
import scipy.interpolate

filename = 'bpass_spectra.txt'
extinctionfile = 'ExtinctionLawPoints.txt' # from R_V = 4.0
pointslist = []
datalist = []
speclist = []

# Constants
h = 4.1357e-15 # Planck's constant [eV s]
c = float(3e8) # speed of light [m/s]

# Read spectra file
f = open(filename, 'r')
rawspectra = f.readlines()
met = re.findall('Z\s=\s(\d*\.\d+)', rawspectra[0])
del rawspectra[0]
for i in range(len(rawspectra)):
    newlist = rawspectra[i].split(' ')
    datalist.append(newlist)

# Read extinction curve data file
rawpoints = open(extinctionfile, 'r').readlines()
for i in range(len(rawpoints)):
    newlst = re.split('(?!\S)\s(?=\S)|(?!\S)\s+(?=\S)', rawpoints[i])
    pointslist.append(newlst)
pointslist = pointslist[3:]
lambdalist = [float(item[0]) for item in pointslist]
k_abslist = [float(item[4]) for item in pointslist]
xvallist = [(c*h)/(lamb*1e-6) for lamb in lambdalist]
k_interp = scipy.interpolate.interp1d(xvallist, k_abslist)

# Create new lists
Elist = [float(item[0]) for item in datalist]
speclambdalist = [h*c*1e9/E for E in Elist]
z1list = [float(item[1]) for item in datalist]
speclist.extend(z1list)
met = met[0]
klist = [None]*len(speclist)
Loutlist = [None]*len(speclist)
Tlist = [None]*len(speclist)

# Define parameters 
b = 2.0 # power used in extinction law (beta)
R = 1.0 # star formation ratw [Msun/yr]
z = 1.0 # redshift
Mgas = 1.0 # mass of gas
Mhalo = 2e41 # mass of dark matter halo

if float(met) > 0.0052:
    DGRlist = [50.0*np.exp(-2.21)*float(met)]*len(speclist)
elif float(met) <= 0.0052:
    DGRlist = [((50.0*float(met))**3.15)*np.exp(-0.96)]*len(speclist)
for i in range(len(speclist)):
    if float(Elist[i]) <= 4.1357e-3: # frequencies <= 10^12 Hz
        klist[i] = 0.1*(float(Elist[i])/(1000.0*h))**b # extinction law [cm^2/g]
    elif float(Elist[i]) > 4.1357e-3: # frequencies > 10^12 Hz
        klist[i] = k_interp(Elist[i]) # interpolated function's value at Elist[i]
print "KLIST (INTERPOLATION) ELEMENTS 0 AND 1000:", klist[0], klist[1000]

The output from this print line is KLIST (INTERPOLATION) ELEMENTS 0 AND 1000 7779.275435560996 58253.589270674354.

curious_cosmo
  • 1,184
  • 1
  • 18
  • 36
  • Maybe share the original version without the function? –  May 03 '18 at 20:27
  • @wowserx Sure, I'll edit that in – curious_cosmo May 03 '18 at 20:32
  • Please read and follow the posting guidelines in the help documentation, as suggested when you created this account. [Minimal, complete, verifiable example](http://stackoverflow.com/help/mcve) applies here. We cannot effectively help you until you post your MCVE code and accurately describe the problem. We should be able to paste your posted code into a text file and reproduce the problem you described. Show the output you got, the output you expected, and your tracing of intermediate results or other useful debugging attempts. – Prune May 03 '18 at 20:37
  • Can you please confirm that you are entering the `interpolate` function (`Elist[i] > 4.1357e-3`) and that `R_V = 4.0` within `process`? – apogalacticon May 03 '18 at 20:44
  • Yes, I already did that in my error check process and can confirm both of those. – curious_cosmo May 03 '18 at 20:46

1 Answers1

1

You are passing i as an argument to interpolate, and then also using i in a loop within interpolate. Once i is used within the for i in range(len(rawpoints)) loop in interpolate, it will be set to some value: len(rawpoints)-1. The interpolate function will then always return the same value k_interp(Elist[i]), which is equivalent to k_interp(Elist[len(rawpoints)-1]). You will need to either define a new variable within your loop (e.g. for not_i in range(len(rawpoints))), or use a different variable for the Elist argument. Consider the following change to interpolate:

def interpolate(R_V, rawpoints, Elist, j):
    pointslist = []
    if R_V == 4.0:
        for i in range(len(rawpoints)):
            newlst = re.split('(?!\S)\s(?=\S)|(?!\S)\s+(?=\S)', rawpoints[i])
            pointslist.append(newlst)
        pointslist = pointslist[3:]
        lambdalist = [float(item[0]) for item in pointslist]
        k_abslist = [float(item[4]) for item in pointslist]
        xvallist = [(c*h)/(lamb*1e-6) for lamb in lambdalist]
        k_interp = scipy.interpolate.interp1d(xvallist, k_abslist)
        return k_interp(Elist[j])
apogalacticon
  • 709
  • 1
  • 9
  • 19