diff --git a/README.md b/README.md index 0c29370458d1d68583963655f6b4b8157fac1a96..fb66b0c24b32eb9d9b20e90328989cf2e9e15998 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ description is loaded, the noise budget can be calculated and plotted: >>> import numpy as np >>> freq = np.logspace(1, 3, 1000) >>> Budget, ifo, freq_, plot_style = gwinc.load_ifo('aLIGO') ->>> ifo = gwinc.precompIFO(freq, ifo) >>> traces = Budget(freq, ifo=ifo).calc_trace() >>> fig = gwinc.plot_noise(freq, traces, **plot_style) >>> fig.show() diff --git a/gwinc/__init__.py b/gwinc/__init__.py index 9511e40e3f0ffe39e688e5d8f5932220fdd8b214..71e5bf03ecade9be17c7c5078708c2bd2a2cd5c4 100644 --- a/gwinc/__init__.py +++ b/gwinc/__init__.py @@ -1,5 +1,4 @@ from .ifo import available_ifos, load_ifo -from .precomp import precompIFO from .struct import Struct from .plot import plot_noise from .io import load_hdf5, save_hdf5 diff --git a/gwinc/__main__.py b/gwinc/__main__.py index b20919da799a681221cac83e851e2ac67b4aedc0..29c2586909109373a647d06812a16fdf47822036 100644 --- a/gwinc/__main__.py +++ b/gwinc/__main__.py @@ -10,7 +10,6 @@ logging.basicConfig(format='%(message)s', level=os.getenv('LOG_LEVEL', logging.INFO)) from .ifo import available_ifos, load_ifo -from .precomp import precompIFO from . import plot_noise from . import io @@ -174,9 +173,6 @@ def main(): # main calculations if not traces: - if ifo: - logging.info("precomputing ifo...") - ifo = precompIFO(freq, ifo) logging.info("calculating budget...") budget = Budget(freq=freq, ifo=ifo) budget.load() diff --git a/gwinc/gwinc.py b/gwinc/gwinc.py index 93b46d958a744bc85904f6e6f7ae62cdf9239152..e4fe68902960d74b61e7aee4e34d1209115a1f79 100644 --- a/gwinc/gwinc.py +++ b/gwinc/gwinc.py @@ -5,12 +5,11 @@ from collections import OrderedDict import logging from . import load_ifo -from .precomp import precompIFO from . import noise from .plot import plot_noise -def gwinc(freq, ifoin, source=None, plot=False, PRfixed=True): +def gwinc(freq, ifo, source=None, plot=False, PRfixed=True): """Calculate strain noise budget for a specified interferometer model. Argument `freq` is the frequency array for which the noises will @@ -31,10 +30,6 @@ def gwinc(freq, ifoin, source=None, plot=False, PRfixed=True): # from just ifo description, without having to specify full budget Budget, ifo_, freq_, plot_style = load_ifo('aLIGO') - # add some precomputed info to the ifo struct - # this implicitly deepcopies and the return value is the copy - ifo = precompIFO(freq, ifoin, PRfixed) - traces = Budget(freq, ifo=ifo).calc_trace() # construct matgwinc-compatible noises structure diff --git a/gwinc/ifo/noises.py b/gwinc/ifo/noises.py index 9dc2212c40d7d4bf6640f6f9ad941d244d05617d..b55f5650f162852b32ec8d97a008f7f198999967 100644 --- a/gwinc/ifo/noises.py +++ b/gwinc/ifo/noises.py @@ -2,11 +2,95 @@ import numpy as np from numpy import pi, sin, exp, sqrt from .. import const +from ..struct import Struct from .. import nb from .. import noise +from .. import suspension ################################################## +def coating_thickness(ifo, optic): + optic = ifo.Optics.get(optic) + if 'CoatLayerOpticalThickness' in optic: + return optic['CoatLayerOpticalThickness'] + T = optic.Transmittance + dL = optic.CoatingThicknessLown + dCap = optic.CoatingThicknessCap + return noise.coatingthermal.getCoatDopt(ifo.Materials, T, dL, dCap=dCap) + + +def arm_cavity(ifo): + L = ifo.Infrastructure.Length + + g1 = 1 - L / ifo.Optics.Curvature.ITM + g2 = 1 - L / ifo.Optics.Curvature.ETM + gcav = sqrt(g1 * g2 * (1 - g1 * g2)) + gden = g1 - 2 * g1 * g2 + g2 + + if (g1 * g2 * (1 - g1 * g2)) <= 0: + raise Exception('Unstable arm cavity g-factors. Change ifo.Optics.Curvature') + elif gcav < 1e-3: + logging.warning('Nearly unstable arm cavity g-factors. Reconsider ifo.Optics.Curvature') + + ws = sqrt(L * ifo.Laser.Wavelength / pi) + w1 = ws * sqrt(abs(g2) / gcav) + w2 = ws * sqrt(abs(g1) / gcav) + + # waist size + w0 = ws * sqrt(gcav / abs(gden)) + zr = pi * w0**2 / ifo.Laser.Wavelength + z1 = L * g2 * (1 - g1) / gden + z2 = L * g1 * (1 - g2) / gden + + # waist, input, output + return w0, w1, w2 + + +def ifo_power(ifo, PRfixed=True): + """Compute power on beamsplitter, finesse, and power recycling factor. + + """ + c = const.c + pin = ifo.Laser.Power + t1 = sqrt(ifo.Optics.ITM.Transmittance) + r1 = sqrt(1 - ifo.Optics.ITM.Transmittance) + r2 = sqrt(1 - ifo.Optics.ETM.Transmittance) + t5 = sqrt(ifo.Optics.PRM.Transmittance) + r5 = sqrt(1 - ifo.Optics.PRM.Transmittance) + loss = ifo.Optics.Loss # single TM loss + bsloss = ifo.Optics.BSLoss + acoat = ifo.Optics.ITM.CoatingAbsorption + pcrit = ifo.Optics.pcrit + + # Finesse, effective number of bounces in cavity, power recycling factor + finesse = 2*pi / (t1**2 + 2*loss) # arm cavity finesse + neff = 2 * finesse / pi + + # Arm cavity reflectivity with finite loss + garm = t1 / (1 - r1*r2*sqrt(1-2*loss)) # amplitude gain wrt input field + rarm = r1 - t1 * r2 * sqrt(1-2*loss) * garm + + if PRfixed: + Tpr = ifo.Optics.PRM.Transmittance # use given value + else: + Tpr = 1-(rarm*sqrt(1-bsloss))**2 # optimal recycling mirror transmission + t5 = sqrt(Tpr) + r5 = sqrt(1 - Tpr) + prfactor = t5**2 / (1 + r5 * rarm * sqrt(1-bsloss))**2 + + pbs = pin * prfactor # BS power from input power + parm = pbs * garm**2 / 2 # arm power from BS power + + thickness = ifo.Optics.ITM.get('Thickness', ifo.Materials.MassThickness) + asub = 1.3 * 2 * thickness * ifo.Optics.SubstrateAbsorption + pbsl = 2 *pcrit / (asub+acoat*neff) # bs power limited by thermal lensing + + if pbs > pbsl: + logging.warning('P_BS exceeds BS Thermal limit!') + + return pbs, parm, finesse, prfactor, Tpr + + def dhdl(f, armlen): """Strain to length conversion for noise power spetra @@ -35,6 +119,44 @@ def dhdl(f, armlen): ################################################## +def precomp_mirror(f, ifo): + ifo.Materials.MirrorVolume = \ + pi * ifo.Materials.MassRadius**2 \ + * ifo.Materials.MassThickness + ifo.Materials.MirrorMass = \ + ifo.Materials.MirrorVolume \ + * ifo.Materials.Substrate.MassDensity + +def precomp_gwinc(f, ifo): + ifo.gwinc = Struct() + pbs, parm, finesse, prfactor, Tpr = ifo_power(ifo) + ifo.gwinc.pbs = pbs + ifo.gwinc.parm = parm + ifo.gwinc.finesse = finesse + ifo.gwinc.prfactor = prfactor + + +def precomp_suspension(f, ifo): + if 'VHCoupling' not in ifo.Suspension: + ifo.Suspension.VHCoupling = Struct() + ifo.Suspension.VHCoupling.theta = ifo.Infrastructure.Length / const.R_earth + + hForce, vForce, hTable, vTable = suspension.suspQuad(f, ifo.Suspension) + try: + # full TF (conventional) + ifo.Suspension.hForce = hForce.fullylossy + ifo.Suspension.vForce = vForce.fullylossy + # TFs with each stage lossy + ifo.Suspension.hForce_singlylossy = hForce.singlylossy + ifo.Suspension.vForce_singlylossy = vForce.singlylossy + except: + ifo.Suspension.hForce = hForce + ifo.Suspension.vForce = vForce + ifo.Suspension.hTable = hTable + ifo.Suspension.vTable = vTable + +################################################## + class Strain(nb.Calibration): def calc(self): dhdl_sqr, sinc_sqr = dhdl(self.freq, self.ifo.Infrastructure.Length) @@ -52,6 +174,8 @@ class QuantumVacuum(nb.Noise): ) def calc(self): + precomp_mirror(self.freq, self.ifo) + precomp_gwinc(self.freq, self.ifo) return noise.quantum.shotrad(self.freq, self.ifo) @@ -65,6 +189,7 @@ class Seismic(nb.Noise): ) def calc(self): + precomp_suspension(self.freq, self.ifo) if 'PlatformMotion' in self.ifo.Seismic: if self.ifo.Seismic.PlatformMotion == 'BSC': nt, nr = noise.seismic.seismic_BSC_ISI(self.freq) @@ -146,6 +271,7 @@ class SuspensionThermal(nb.Noise): ) def calc(self): + precomp_suspension(self.freq, self.ifo) n = noise.suspensionthermal.suspension_thermal(self.freq, self.ifo.Suspension) return n * 4 @@ -162,10 +288,9 @@ class CoatingBrownian(nb.Noise): def calc(self): wavelength = self.ifo.Laser.Wavelength materials = self.ifo.Materials - wBeam_ITM = self.ifo.Optics.ITM.BeamRadius - wBeam_ETM = self.ifo.Optics.ETM.BeamRadius - dOpt_ITM = self.ifo.Optics.ITM.CoatLayerOpticalThickness - dOpt_ETM = self.ifo.Optics.ETM.CoatLayerOpticalThickness + w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo) + dOpt_ITM = coating_thickness(self.ifo, 'ITM') + dOpt_ETM = coating_thickness(self.ifo, 'ETM') nITM = noise.coatingthermal.coating_brownian( self.freq, materials, wavelength, wBeam_ITM, dOpt_ITM) nETM = noise.coatingthermal.coating_brownian( @@ -186,10 +311,9 @@ class CoatingThermoOptic(nb.Noise): def calc(self): wavelength = self.ifo.Laser.Wavelength materials = self.ifo.Materials - wBeam_ITM = self.ifo.Optics.ITM.BeamRadius - wBeam_ETM = self.ifo.Optics.ETM.BeamRadius - dOpt_ITM = self.ifo.Optics.ITM.CoatLayerOpticalThickness - dOpt_ETM = self.ifo.Optics.ETM.CoatLayerOpticalThickness + w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo) + dOpt_ITM = coating_thickness(self.ifo, 'ITM') + dOpt_ETM = coating_thickness(self.ifo, 'ETM') nITM, junk1, junk2, junk3 = noise.coatingthermal.coating_thermooptic( self.freq, materials, wavelength, wBeam_ITM, dOpt_ITM[:]) nETM, junk1, junk2, junk3 = noise.coatingthermal.coating_thermooptic( @@ -209,9 +333,11 @@ class ITMThermoRefractive(nb.Noise): def calc(self): L = self.ifo.Infrastructure.Length - gPhase = self.ifo.gwinc.finesse * 2/np.pi + finesse = ifo_power(self.ifo)[2] + gPhase = finesse * 2/np.pi + w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo) n = noise.substratethermal.substrate_thermorefractive( - self.freq, self.ifo.Materials, self.ifo.Optics.ITM.BeamRadius) + self.freq, self.ifo.Materials, wBeam_ITM) return n * 2 / (gPhase*L)**2 @@ -227,9 +353,11 @@ class ITMCarrierDensity(nb.Noise): def calc(self): L = self.ifo.Infrastructure.Length - gPhase = self.ifo.gwinc.finesse * 2/np.pi + finesse = ifo_power(self.ifo)[2] + gPhase = finesse * 2/np.pi + w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo) n = noise.substratethermal.substrate_carrierdensity( - self.freq, self.ifo.Materials, self.ifo.Optics.ITM.BeamRadius) + self.freq, self.ifo.Materials, wBeam_ITM) return n * 2 / (gPhase*L)**2 @@ -244,10 +372,11 @@ class SubstrateBrownian(nb.Noise): ) def calc(self): + w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo) nITM = noise.substratethermal.substrate_brownian( - self.freq, self.ifo.Materials, self.ifo.Optics.ITM.BeamRadius) + self.freq, self.ifo.Materials, wBeam_ITM) nETM = noise.substratethermal.substrate_brownian( - self.freq, self.ifo.Materials, self.ifo.Optics.ETM.BeamRadius) + self.freq, self.ifo.Materials, wBeam_ETM) return (nITM + nETM) * 2 @@ -262,10 +391,11 @@ class SubstrateThermoElastic(nb.Noise): ) def calc(self): + w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo) nITM = noise.substratethermal.substrate_thermoelastic( - self.freq, self.ifo.Materials, self.ifo.Optics.ITM.BeamRadius) + self.freq, self.ifo.Materials, wBeam_ITM) nETM = noise.substratethermal.substrate_thermoelastic( - self.freq, self.ifo.Materials, self.ifo.Optics.ETM.BeamRadius) + self.freq, self.ifo.Materials, wBeam_ETM) return (nITM + nETM) * 2 diff --git a/gwinc/precomp.py b/gwinc/precomp.py deleted file mode 100644 index 554834c23e038eeb05e8b397a8daeb40b650687b..0000000000000000000000000000000000000000 --- a/gwinc/precomp.py +++ /dev/null @@ -1,183 +0,0 @@ -from __future__ import division -from numpy import pi, sqrt, sin, exp -from scipy.io import loadmat -import logging -import copy - -from . import const -from . import suspension -from .struct import Struct -from .noise.coatingthermal import getCoatDopt - - -def precompIFO(f, ifoin, PRfixed=True): - """Add precomputed data to the IFO model. - - To prevent recomputation of these precomputed data, if the - ifo argument contains ifo.gwinc.PRfixed, and this matches - the argument PRfixed, no changes are made. - - This function DOES NOT modify IFO. Its return value is a copy of - IFO with precomputations filled out. - - """ - ifo = copy.deepcopy(ifoin) - - if 'gwinc' not in ifo: - ifo.gwinc = Struct() - - ifo.gwinc.PRfixed = PRfixed - - ############################## - # derived temp - - if 'Temp' not in ifo.Materials.Substrate: - ifo.Materials.Substrate.Temp = ifo.Infrastructure.Temp - - ############################## - # suspension vertical-horizontal coupling - - if 'VHCoupling' not in ifo.Suspension: - ifo.Suspension.VHCoupling = Struct() - ifo.Suspension.VHCoupling.theta = ifo.Infrastructure.Length / const.R_earth - - ############################## - # optics values - - # calculate optics' parameters - ifo.Materials.MirrorVolume = pi*ifo.Materials.MassRadius**2 * \ - ifo.Materials.MassThickness - ifo.Materials.MirrorMass = ifo.Materials.MirrorVolume * \ - ifo.Materials.Substrate.MassDensity - ifo.Optics.ITM.Thickness = ifo.Materials.MassThickness - - # coating layer optical thicknesses - mevans 2 May 2008 - if 'CoatLayerOpticalThickness' not in ifo.Optics.ITM: - T = ifo.Optics.ITM.Transmittance - dL = ifo.Optics.ITM.CoatingThicknessLown - dCap = ifo.Optics.ITM.CoatingThicknessCap - ifo.Optics.ITM.CoatLayerOpticalThickness = getCoatDopt(ifo.Materials, T, dL, dCap=dCap) - T = ifo.Optics.ETM.Transmittance - dL = ifo.Optics.ETM.CoatingThicknessLown - dCap = ifo.Optics.ETM.CoatingThicknessCap - ifo.Optics.ETM.CoatLayerOpticalThickness = getCoatDopt(ifo.Materials, T, dL, dCap=dCap) - - ############################## - # beam parameters - - armlen = ifo.Infrastructure.Length - - # g-factors - g1 = 1 - armlen / ifo.Optics.Curvature.ITM - g2 = 1 - armlen / ifo.Optics.Curvature.ETM - gcav = sqrt(g1 * g2 * (1 - g1 * g2)) - gden = g1 - 2 * g1 * g2 + g2 - - if (g1 * g2 * (1 - g1 * g2)) <= 0: - raise Exception('Unstable arm cavity g-factors. Change ifo.Optics.Curvature') - elif gcav < 1e-3: - logging.warning('Nearly unstable arm cavity g-factors. Reconsider ifo.Optics.Curvature') - - ws = sqrt(armlen * ifo.Laser.Wavelength / pi) - w1 = ws * sqrt(abs(g2) / gcav) - w2 = ws * sqrt(abs(g1) / gcav) - - # waist size - w0 = ws * sqrt(gcav / abs(gden)) - zr = pi * w0**2 / ifo.Laser.Wavelength - z1 = armlen * g2 * (1 - g1) / gden - z2 = armlen * g1 * (1 - g2) / gden - - ifo.Optics.ITM.BeamRadius = w1 - ifo.Optics.ETM.BeamRadius = w2 - - ############################## - # calc power and IFO parameters - - pbs, parm, finesse, prfactor, Tpr = precompPower(ifo, PRfixed) - - ifo.gwinc.pbs = pbs - ifo.gwinc.parm = parm - ifo.gwinc.finesse = finesse - ifo.gwinc.prfactor = prfactor - ifo.gwinc.gITM = g1 - ifo.gwinc.gETM = g2 - ifo.gwinc.BeamWaist = w0 - ifo.gwinc.BeamRayleighRange = zr - ifo.gwinc.BeamWaistToITM = z1 - ifo.gwinc.BeamWaistToETM = z2 - ifo.Optics.PRM.Transmittance = Tpr - - ############################## - # saved seismic spectrum - - if 'darmSeiSusFile' in ifo.Seismic and ifo.Seismic.darmSeiSusFile: - darmsei = loadmat(ifo.Seismic.darmSeiSusFile) - ifo.Seismic.darmseis_f = darmsei['darmseis_f'][0] - ifo.Seismic.darmseis_x = darmsei['darmseis_x'][0] - - # -------------------------------------------------------- - # Suspension - # if the suspension code supports different temps for the stages - fname = eval('suspension.susp{}'.format(ifo.Suspension.Type)) - hForce, vForce, hTable, vTable = fname(f, ifo.Suspension) - - try: - # full TF (conventional) - ifo.Suspension.hForce = hForce.fullylossy - ifo.Suspension.vForce = vForce.fullylossy - # TFs with each stage lossy - ifo.Suspension.hForce_singlylossy = hForce.singlylossy - ifo.Suspension.vForce_singlylossy = vForce.singlylossy - except: - ifo.Suspension.hForce = hForce - ifo.Suspension.vForce = vForce - - ifo.Suspension.hTable = hTable - ifo.Suspension.vTable = vTable - - return ifo - - -def precompPower(ifo, PRfixed=True): - """Compute power on beamsplitter, finesse, and power recycling factor. - - """ - c = const.c - pin = ifo.Laser.Power - t1 = sqrt(ifo.Optics.ITM.Transmittance) - r1 = sqrt(1 - ifo.Optics.ITM.Transmittance) - r2 = sqrt(1 - ifo.Optics.ETM.Transmittance) - t5 = sqrt(ifo.Optics.PRM.Transmittance) - r5 = sqrt(1 - ifo.Optics.PRM.Transmittance) - loss = ifo.Optics.Loss # single TM loss - bsloss = ifo.Optics.BSLoss - acoat = ifo.Optics.ITM.CoatingAbsorption - pcrit = ifo.Optics.pcrit - - # Finesse, effective number of bounces in cavity, power recycling factor - finesse = 2*pi / (t1**2 + 2*loss) # arm cavity finesse - neff = 2 * finesse / pi - - # Arm cavity reflectivity with finite loss - garm = t1 / (1 - r1*r2*sqrt(1-2*loss)) # amplitude gain wrt input field - rarm = r1 - t1 * r2 * sqrt(1-2*loss) * garm - - if PRfixed: - Tpr = ifo.Optics.PRM.Transmittance # use given value - else: - Tpr = 1-(rarm*sqrt(1-bsloss))**2 # optimal recycling mirror transmission - t5 = sqrt(Tpr) - r5 = sqrt(1 - Tpr) - prfactor = t5**2 / (1 + r5 * rarm * sqrt(1-bsloss))**2 - - pbs = pin * prfactor # BS power from input power - parm = pbs * garm**2 / 2 # arm power from BS power - - asub = 1.3*2*ifo.Optics.ITM.Thickness*ifo.Optics.SubstrateAbsorption - pbsl = 2*pcrit/(asub+acoat*neff) # bs power limited by thermal lensing - - if pbs > pbsl: - logging.warning('P_BS exceeds BS Thermal limit!') - - return pbs, parm, finesse, prfactor, Tpr