Source code for nexoclom.LOSResult

import os.path
import numpy as np
import pandas as pd
import psycopg2
import pickle
import astropy.units as u
from solarsystem import SSObject
from MESSENGERuvvs import MESSENGERdata
from .ModelResults import (ModelResult, read_format, results_loadfile,
                           results_packet_weighting)


[docs]class LOSResult(ModelResult): def __init__(self, inputs, data, quantity, dphi=3*u.deg, filenames=None): self.type = 'LineOfSight' self.species = inputs.options.atom self.quantity = quantity # column, radiance self.origin = inputs.geometry.planet self.unit = u.def_unit('R_' + self.origin.object, self.origin.radius) self.dphi = dphi ModelResult.__init__(self, inputs) if isinstance(filenames, str): print('Setting filenames breaks calibration.') self.filenames = [filenames] elif isinstance(filenames, list): print('Setting filenames breaks calibration.') self.filenames = filenames else: pass if self.quantity == 'radiance': self.mechanism = 'resscat', if inputs.options.atom == 'Na': self.wavelength = 5891*u.AA, 5897*u.AA elif inputs.options.atom == 'Ca': self.wavelength = 4227*u.AA, elif inputs.options.atom == 'Mg': self.wavelength = 2852*u.AA, else: assert 0, f'No default wavelength for {input.options.atom}' else: pass nspec = len(data.x) self.radiance = np.zeros(nspec) self.ninview = np.zeros(nspec, dtype=int) for j,outfile in enumerate(self.filenames): # Search to see if it is already done radiance_, packets_ = self.restore(data, outfile) if radiance_ is None: radiance_, packets_ = self.create_model(data, outfile) print(f'Completed model {j+1} of {len(self.filenames)}') else: print(f'Model {j+1} of {len(self.filenames)} ' 'previously completed.') self.radiance += radiance_ self.packets += packets_ self.radiance = self.radiance * self.atoms_per_packet.value * u.R
[docs] def save(self, data, fname, radiance, packets): # Determine if the model can be saved. # Criteria: 1 complete orbit, nothing more. orbits = set(data.orbit) orb = orbits.pop() if len(orbits) != 0: print('Model spans more than one orbit. Cannot be saved.') else: mdata = MESSENGERdata(self.species, f'orbit = {orb}') if len(mdata) != len(data): print('Model does not contain the complete orbit. ' 'Cannot be saved.') else: con = psycopg2.connect(database=self.inputs.database) con.autocommit = True cur = con.cursor() # Determine the id of the outputfile idnum_ = pd.read_sql(f'''SELECT idnum FROM outputfile WHERE filename='{fname}' ''', con) idnum = int(idnum_.idnum[0]) # Insert the model into the database if self.quantity == 'radiance': mech = ', '.join(sorted([m for m in self.mechanism])) wave_ = sorted([w.value for w in self.wavelength]) wave = ', '.join([str(w) for w in wave_]) else: mech = None wave = None cur.execute(f'''INSERT into uvvsmodels (out_idnum, quantity, orbit, dphi, mechanism, wavelength) values (%s, %s, %s, %s, %s, %s)''', (idnum, self.quantity, orb, self.dphi.value, mech, wave)) # Determine the savefile name idnum_ = pd.read_sql('''SELECT idnum FROM uvvsmodels WHERE filename is NULL''', con) assert len(idnum_) == 1 idnum = int(idnum_.idnum[0]) savefile = os.path.join(os.path.dirname(fname), f'model.orbit{orb:04}.{idnum}.pkl') with open(savefile, 'wb') as f: pickle.dump((radiance, packets), f) cur.execute(f'''UPDATE uvvsmodels SET filename=%s WHERE idnum=%s''', (savefile, idnum))
[docs] def restore(self, data, fname): # Determine if the model can be restored. # Criteria: 1 complete orbit, nothing more. orbits = set(data.orbit) orb = orbits.pop() if len(orbits) != 0: print('Model spans more than one orbit. Cannot be saved.') radiance, packets = None, None else: mdata = MESSENGERdata(self.species, f'orbit = {orb}') if len(mdata) != len(data): print('Model does not contain the complete orbit. ' 'Cannot be saved.') radiance, packets = None, None else: con = psycopg2.connect(database=self.inputs.database) con.autocommit = True # Determine the id of the outputfile idnum_ = pd.read_sql(f'''SELECT idnum FROM outputfile WHERE filename='{fname}' ''', con) oid = idnum_.idnum[0] if self.quantity == 'radiance': mech = ("mechanism = '" + ", ".join(sorted([m for m in self.mechanism])) + "'") wave_ = sorted([w.value for w in self.wavelength]) wave = ("wavelength = '" + ", ".join([str(w) for w in wave_]) + "'") else: mech = 'mechanism is NULL' wave = 'wavelength is NULL' result = pd.read_sql( f'''SELECT filename FROM uvvsmodels WHERE out_idnum={oid} and quantity = '{self.quantity}' and orbit = {orb} and dphi = {self.dphi.value} and {mech} and {wave}''', con) assert len(result) <= 1 if len(result) == 1: savefile = result.filename[0] with open(savefile, 'rb') as f: radiance, packets = pickle.load(f) else: radiance, packets = None, None return radiance, packets
[docs] def create_model(self, data, outfile): # distance of s/c from planet dist_from_plan = np.sqrt(data.x**2 + data.y**2 + data.z**2) # Angle between look direction and planet. ang = np.arccos((-data.x*data.xbore - data.y*data.ybore - data.z*data.zbore)/dist_from_plan) # Check to see if look direction intersects the planet anywhere asize_plan = np.arcsin(1./dist_from_plan) # Don't worry about lines of sight that don't hit the planet dist_from_plan[ang > asize_plan] = 1e30 # Load the outputfile output = results_loadfile(outfile) radvel_sun = output.vy + output.vrplanet # Will base shadow on line of sight, not the packets out_of_shadow = np.ones_like(output.x) weight = results_packet_weighting(self, radvel_sun, output.frac, out_of_shadow, output.aplanet) xx_, yy_, zz_ = (np.zeros((2,len(data))), np.zeros((2,len(data))), np.zeros((2,len(data)))) xx_[1,:], yy_[1,:], zz_[1,:] = (data.xbore*10, data.ybore*10, data.zbore*10) xx = (data.x[np.newaxis,:] + xx_) yy = (data.y[np.newaxis,:] + yy_) zz = (data.z[np.newaxis,:] + zz_) xx_min = np.min(xx-0.5, axis=0)*self.unit yy_min = np.min(yy-0.5, axis=0)*self.unit zz_min = np.min(zz-0.5, axis=0)*self.unit xx_max = np.max(xx+0.5, axis=0)*self.unit yy_max = np.max(yy+0.5, axis=0)*self.unit zz_max = np.max(zz+0.5, axis=0)*self.unit radiance, packets = np.zeros(len(data)), np.zeros(len(data)) for i,row in data.iterrows(): # This removes the packets that aren't close to the los mask = ((output.x >= xx_min[i]) & (output.x <= xx_max[i]) & (output.y >= yy_min[i]) & (output.y <= yy_max[i]) & (output.z >= zz_min[i]) & (output.z <= zz_max[i])) x_, y_, z_, w_, rvsun_ = (output.x[mask], output.y[mask], output.z[mask], weight[mask], radvel_sun[mask]) # Distance from the spacecraft xpr = x_ - row.x*self.unit ypr = y_ - row.y*self.unit zpr = z_ - row.z*self.unit rpr = np.sqrt(xpr**2 + ypr**2 + zpr**2) # Packet-s/c boresight angle costheta = (xpr*row.xbore + ypr*row.ybore + zpr*row.zbore)/rpr costheta[costheta > 1] = 1. costheta[costheta < -1] = -1. inview = ((costheta >= np.cos(self.dphi)) & (w_ > 0) & (rpr < dist_from_plan[i]*self.unit)) if np.any(inview): Apix = np.pi * (rpr[inview]*np.sin(self.dphi))**2 wtemp = w_[inview]/Apix.to(u.cm**2) wtemp = wtemp.value if self.quantity == 'radiance': # Determine if any packets are in shadow # Projection of packet onto LOS losrad = rpr[inview] * costheta[inview] # Point along LOS the packet represents xhit = row.x + row.xbore*losrad.value yhit = row.y + row.ybore*losrad.value zhit = row.z + row.zbore*losrad.value rhohit = xhit**2 + zhit**2 out_of_shadow = (rhohit > 1) | (yhit < 0) wtemp *= out_of_shadow radiance[i] = np.sum(wtemp) packets[i] = np.sum(inview) del output self.save(data, outfile, radiance, packets) return radiance, packets