diff --git a/gwinc/ifo/Aplus/__init__.py b/gwinc/ifo/Aplus/__init__.py index a4dedee87ba3188a855be601e365ff98c3b833ae..bef20eb75510d1f4d7babac3262d6f89852713bf 100644 --- a/gwinc/ifo/Aplus/__init__.py +++ b/gwinc/ifo/Aplus/__init__.py @@ -1,8 +1,10 @@ -from gwinc.ifo.noises import * from gwinc.ifo import PLOT_STYLE +from gwinc import noise +from gwinc import nb +from gwinc.ifo.noises import Strain -class QuantumVacuum(nb.Budget): +class Quantum(nb.Budget): """Quantum Vacuum """ @@ -12,13 +14,13 @@ class QuantumVacuum(nb.Budget): ) noises = [ - QuantumVacuumAS, - QuantumVacuumArm, - QuantumVacuumSEC, - QuantumVacuumFilterCavity, - QuantumVacuumInjection, - QuantumVacuumReadout, - QuantumVacuumQuadraturePhase, + noise.quantum.AS, + noise.quantum.Arm, + noise.quantum.SEC, + noise.quantum.FilterCavity, + noise.quantum.Injection, + noise.quantum.Readout, + noise.quantum.QuadraturePhase, ] @@ -27,15 +29,15 @@ class Aplus(nb.Budget): name = 'A+' noises = [ - QuantumVacuum, - Seismic, - Newtonian, - SuspensionThermal, - CoatingBrownian, - CoatingThermoOptic, - SubstrateBrownian, - SubstrateThermoElastic, - ExcessGas, + Quantum, + noise.seismic.Seismic, + noise.newtonian.Newtonian, + noise.suspensionthermal.SuspensionThermal, + noise.coatingthermal.CoatingBrownian, + noise.coatingthermal.CoatingThermoOptic, + noise.substratethermal.SubstrateBrownian, + noise.substratethermal.SubstrateThermoElastic, + noise.residualgas.ResidualGas, ] calibrations = [ diff --git a/gwinc/ifo/CE1/__init__.py b/gwinc/ifo/CE1/__init__.py index 586d3f73218721c748e49cfe98ac41347822859b..04594b3d33fc9b77c1ff80698c54bd027ee7a57a 100644 --- a/gwinc/ifo/CE1/__init__.py +++ b/gwinc/ifo/CE1/__init__.py @@ -1,8 +1,10 @@ -from gwinc.ifo.noises import * from gwinc.ifo import PLOT_STYLE +from gwinc import noise +from gwinc import nb +from gwinc.ifo.noises import Strain -class QuantumVacuum(nb.Budget): +class Quantum(nb.Budget): """Quantum Vacuum """ @@ -12,13 +14,13 @@ class QuantumVacuum(nb.Budget): ) noises = [ - QuantumVacuumAS, - QuantumVacuumArm, - QuantumVacuumSEC, - QuantumVacuumFilterCavity, - QuantumVacuumInjection, - QuantumVacuumReadout, - QuantumVacuumQuadraturePhase, + noise.quantum.AS, + noise.quantum.Arm, + noise.quantum.SEC, + noise.quantum.FilterCavity, + noise.quantum.Injection, + noise.quantum.Readout, + noise.quantum.QuadraturePhase, ] @@ -35,9 +37,9 @@ class Newtonian(nb.Budget): ) noises = [ - NewtonianRayleigh, - NewtonianBody, - NewtonianInfrasound, + noise.newtonian.Rayleigh, + noise.newtonian.Body, + noise.newtonian.Infrasound, ] @@ -54,8 +56,8 @@ class Coating(nb.Budget): ) noises = [ - CoatingBrownian, - CoatingThermoOptic, + noise.coatingthermal.CoatingBrownian, + noise.coatingthermal.CoatingThermoOptic, ] @@ -72,8 +74,8 @@ class Substrate(nb.Budget): ) noises = [ - SubstrateBrownian, - SubstrateThermoElastic, + noise.substratethermal.SubstrateBrownian, + noise.substratethermal.SubstrateThermoElastic, ] @@ -82,13 +84,13 @@ class CE1(nb.Budget): name = 'Cosmic Explorer 1' noises = [ - QuantumVacuum, - Seismic, + Quantum, + noise.seismic.Seismic, Newtonian, - SuspensionThermal, + noise.suspensionthermal.SuspensionThermal, Coating, Substrate, - ExcessGas, + noise.residualgas.ResidualGas, ] calibrations = [ diff --git a/gwinc/ifo/CE2silica/__init__.py b/gwinc/ifo/CE2silica/__init__.py index 8cbfa37fea241ec50a648a14fe11c37ed3bc7ec4..4af888b20e148aa2a0da83db3655c37fbca17b20 100644 --- a/gwinc/ifo/CE2silica/__init__.py +++ b/gwinc/ifo/CE2silica/__init__.py @@ -1,8 +1,10 @@ -from gwinc.ifo.noises import * from gwinc.ifo import PLOT_STYLE +from gwinc import noise +from gwinc import nb +from gwinc.ifo.noises import Strain -class QuantumVacuum(nb.Budget): +class Quantum(nb.Budget): """Quantum Vacuum """ @@ -12,13 +14,13 @@ class QuantumVacuum(nb.Budget): ) noises = [ - QuantumVacuumAS, - QuantumVacuumArm, - QuantumVacuumSEC, - QuantumVacuumFilterCavity, - QuantumVacuumInjection, - QuantumVacuumReadout, - QuantumVacuumQuadraturePhase, + noise.quantum.AS, + noise.quantum.Arm, + noise.quantum.SEC, + noise.quantum.FilterCavity, + noise.quantum.Injection, + noise.quantum.Readout, + noise.quantum.QuadraturePhase, ] @@ -35,9 +37,9 @@ class Newtonian(nb.Budget): ) noises = [ - NewtonianRayleigh, - NewtonianBody, - NewtonianInfrasound, + noise.newtonian.Rayleigh, + noise.newtonian.Body, + noise.newtonian.Infrasound, ] @@ -54,8 +56,8 @@ class Coating(nb.Budget): ) noises = [ - CoatingBrownian, - CoatingThermoOptic, + noise.coatingthermal.CoatingBrownian, + noise.coatingthermal.CoatingThermoOptic, ] @@ -72,8 +74,8 @@ class Substrate(nb.Budget): ) noises = [ - SubstrateBrownian, - SubstrateThermoElastic, + noise.substratethermal.SubstrateBrownian, + noise.substratethermal.SubstrateThermoElastic, ] @@ -82,13 +84,13 @@ class CE2silica(nb.Budget): name = 'Cosmic Explorer 2 (Silica)' noises = [ - QuantumVacuum, - Seismic, + Quantum, + noise.seismic.Seismic, Newtonian, - SuspensionThermal, + noise.suspensionthermal.SuspensionThermal, Coating, Substrate, - ExcessGas, + noise.residualgas.ResidualGas, ] calibrations = [ diff --git a/gwinc/ifo/CE2silicon/__init__.py b/gwinc/ifo/CE2silicon/__init__.py index 1c43d3b7bc7322097625ec9142013c2ccce452c2..2dca9814ce0289e485985c4d54261afa18b44ec4 100644 --- a/gwinc/ifo/CE2silicon/__init__.py +++ b/gwinc/ifo/CE2silicon/__init__.py @@ -1,8 +1,10 @@ -from gwinc.ifo.noises import * from gwinc.ifo import PLOT_STYLE +from gwinc import noise +from gwinc import nb +from gwinc.ifo.noises import Strain -class QuantumVacuum(nb.Budget): +class Quantum(nb.Budget): """Quantum Vacuum """ @@ -12,13 +14,13 @@ class QuantumVacuum(nb.Budget): ) noises = [ - QuantumVacuumAS, - QuantumVacuumArm, - QuantumVacuumSEC, - QuantumVacuumFilterCavity, - QuantumVacuumInjection, - QuantumVacuumReadout, - QuantumVacuumQuadraturePhase, + noise.quantum.AS, + noise.quantum.Arm, + noise.quantum.SEC, + noise.quantum.FilterCavity, + noise.quantum.Injection, + noise.quantum.Readout, + noise.quantum.QuadraturePhase, ] @@ -35,9 +37,9 @@ class Newtonian(nb.Budget): ) noises = [ - NewtonianRayleigh, - NewtonianBody, - NewtonianInfrasound, + noise.newtonian.Rayleigh, + noise.newtonian.Body, + noise.newtonian.Infrasound, ] @@ -54,8 +56,8 @@ class Coating(nb.Budget): ) noises = [ - CoatingBrownian, - CoatingThermoOptic, + noise.coatingthermal.CoatingBrownian, + noise.coatingthermal.CoatingThermoOptic, ] @@ -72,9 +74,9 @@ class Substrate(nb.Budget): ) noises = [ - ITMThermoRefractive, - SubstrateBrownian, - SubstrateThermoElastic, + noise.substratethermal.ITMThermoRefractive, + noise.substratethermal.SubstrateBrownian, + noise.substratethermal.SubstrateThermoElastic, ] @@ -83,13 +85,13 @@ class CE2silicon(nb.Budget): name = 'Cosmic Explorer 2 (Silicon)' noises = [ - QuantumVacuum, - Seismic, + Quantum, + noise.seismic.Seismic, Newtonian, - SuspensionThermal, + noise.suspensionthermal.SuspensionThermal, Coating, Substrate, - ExcessGas, + noise.residualgas.ResidualGas, ] calibrations = [ diff --git a/gwinc/ifo/Voyager/__init__.py b/gwinc/ifo/Voyager/__init__.py index 41612d3feb03e8fc846a99665430188741e8b53b..c7dcf2e1dab377592d44136a32050d4f33eb1fd6 100644 --- a/gwinc/ifo/Voyager/__init__.py +++ b/gwinc/ifo/Voyager/__init__.py @@ -1,8 +1,10 @@ -from gwinc.ifo.noises import * from gwinc.ifo import PLOT_STYLE +from gwinc import noise +from gwinc import nb +from gwinc.ifo.noises import Strain -class QuantumVacuum(nb.Budget): +class Quantum(nb.Budget): """Quantum Vacuum """ @@ -12,12 +14,12 @@ class QuantumVacuum(nb.Budget): ) noises = [ - QuantumVacuumAS, - QuantumVacuumArm, - QuantumVacuumSEC, - QuantumVacuumFilterCavity, - QuantumVacuumInjection, - QuantumVacuumReadout, + noise.quantum.AS, + noise.quantum.Arm, + noise.quantum.SEC, + noise.quantum.FilterCavity, + noise.quantum.Injection, + noise.quantum.Readout, ] @@ -26,16 +28,16 @@ class Voyager(nb.Budget): name = 'Voyager' noises = [ - QuantumVacuum, - Seismic, - Newtonian, - SuspensionThermal, - CoatingBrownian, - CoatingThermoOptic, - ITMThermoRefractive, - SubstrateBrownian, - SubstrateThermoElastic, - ExcessGas, + Quantum, + noise.seismic.Seismic, + noise.newtonian.Newtonian, + noise.suspensionthermal.SuspensionThermal, + noise.coatingthermal.CoatingBrownian, + noise.coatingthermal.CoatingThermoOptic, + noise.substratethermal.ITMThermoRefractive, + noise.substratethermal.SubstrateBrownian, + noise.substratethermal.SubstrateThermoElastic, + noise.residualgas.ResidualGas, ] calibrations = [ diff --git a/gwinc/ifo/aLIGO/__init__.py b/gwinc/ifo/aLIGO/__init__.py index 2e299e53626f3c602d2b3a9cf2c0b15949ebc727..5b0d2457d0612670ee196cf805fb24c14a1ffd7a 100644 --- a/gwinc/ifo/aLIGO/__init__.py +++ b/gwinc/ifo/aLIGO/__init__.py @@ -1,8 +1,10 @@ -from gwinc.ifo.noises import * from gwinc.ifo import PLOT_STYLE +from gwinc import noise +from gwinc import nb +from gwinc.ifo.noises import Strain -class QuantumVacuum(nb.Budget): +class Quantum(nb.Budget): """Quantum Vacuum """ @@ -12,10 +14,10 @@ class QuantumVacuum(nb.Budget): ) noises = [ - QuantumVacuumAS, - QuantumVacuumArm, - QuantumVacuumSEC, - QuantumVacuumReadout, + noise.quantum.AS, + noise.quantum.Arm, + noise.quantum.SEC, + noise.quantum.Readout, ] @@ -24,15 +26,15 @@ class aLIGO(nb.Budget): name = 'Advanced LIGO' noises = [ - QuantumVacuum, - Seismic, - Newtonian, - SuspensionThermal, - CoatingBrownian, - CoatingThermoOptic, - SubstrateBrownian, - SubstrateThermoElastic, - ExcessGas, + Quantum, + noise.seismic.Seismic, + noise.newtonian.Newtonian, + noise.suspensionthermal.SuspensionThermal, + noise.coatingthermal.CoatingBrownian, + noise.coatingthermal.CoatingThermoOptic, + noise.substratethermal.SubstrateBrownian, + noise.substratethermal.SubstrateThermoElastic, + noise.residualgas.ResidualGas, ] calibrations = [ diff --git a/gwinc/ifo/noises.py b/gwinc/ifo/noises.py index 7666a1611d0912334fc5eacd907a53d4d6a4947b..1e48bacc4f70ec3a109fcc459bf3fa5908e4c3c1 100644 --- a/gwinc/ifo/noises.py +++ b/gwinc/ifo/noises.py @@ -1,4 +1,3 @@ -import copy import numpy as np from numpy import pi, sin, exp, sqrt @@ -6,7 +5,6 @@ from .. import logger from .. import const from ..struct import Struct from .. import nb -from .. import noise from .. import suspension @@ -14,30 +12,6 @@ from .. import suspension # helper functions ############################################################ -def mirror_struct(ifo, tm): - """Create a "mirror" Struct for a LIGO core optic - - This is a copy of the ifo.Materials Struct, containing Substrate - and Coating sub-Structs, as well as some basic geometrical - properties of the optic. - - """ - # NOTE: we deepcopy this struct since we'll be modifying it (and - # it would otherwise be stored by reference) - mirror = copy.deepcopy(ifo.Materials) - optic = ifo.Optics.get(tm) - if 'CoatLayerOpticalThickness' in optic: - mirror.Coating.dOpt = optic['CoatLayerOpticalThickness'] - else: - T = optic.Transmittance - dL = optic.CoatingThicknessLown - dCap = optic.CoatingThicknessCap - mirror.Coating.dOpt = noise.coatingthermal.getCoatDopt(mirror, T, dL, dCap=dCap) - mirror.update(optic) - mirror.MassVolume = pi * mirror.MassRadius**2 * mirror.MassThickness - mirror.MirrorMass = mirror.MassVolume * mirror.Substrate.MassDensity - return mirror - def arm_cavity(ifo): L = ifo.Infrastructure.Length @@ -167,557 +141,3 @@ class Force(nb.Calibration): def calc(self): mass = mirror_struct(self.ifo, 'ETM').MirrorMass return (mass * (2*pi*self.freq)**2)**2 - - -############################################################ -# noise sources -############################################################ - -######################### -# quantum -######################### - -class QuantumVacuum(nb.Noise): - """Quantum Vacuum - - """ - style = dict( - label='Quantum Vacuum', - color='#ad03de', - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - total = np.zeros_like(quantum.ASvac) - for nn in quantum.values(): - total += nn - return total - - -class QuantumVacuumAS(nb.Noise): - """Quantum vacuum from the AS port - - """ - style = dict( - label='AS Port Vacuum', - color='xkcd:emerald green' - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - return quantum.ASvac - - -class QuantumVacuumArm(nb.Noise): - """Quantum vacuum due to arm cavity loss - - """ - style = dict( - label='Arm Loss', - color='xkcd:orange brown' - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - return quantum.Arm - - -class QuantumVacuumSEC(nb.Noise): - """Quantum vacuum due to SEC loss - - """ - style = dict( - label='SEC Loss', - color='xkcd:cerulean' - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - return quantum.SEC - - -class QuantumVacuumFilterCavity(nb.Noise): - """Quantum vacuum due to filter cavity loss - - """ - style = dict( - label='Filter Cavity Loss', - color='xkcd:goldenrod' - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - return quantum.FC - - -class QuantumVacuumInjection(nb.Noise): - """Quantum vacuum due to injection loss - - """ - style = dict( - label='Injection Loss', - color='xkcd:fuchsia' - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - return quantum.Injection - - -class QuantumVacuumReadout(nb.Noise): - """Quantum vacuum due to readout loss - - """ - style = dict( - label='Readout Loss', - color='xkcd:mahogany' - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - return quantum.PD - - -class QuantumVacuumQuadraturePhase(nb.Noise): - """Quantum vacuum noise due to quadrature phase noise - """ - style = dict( - label='Quadrature Phase', - color='xkcd:slate' - ) - - @nb.precomp(quantum=noise.quantum.precomp_quantum) - def calc(self, quantum): - return quantum.Phase - - -class StandardQuantumLimit(nb.Noise): - """Standard Quantum Limit - - """ - style = dict( - label="Standard Quantum Limit", - color="#000000", - linestyle=":", - ) - - def calc(self): - ETM = mirror_struct(self.ifo, 'ETM') - return 8 * const.hbar / (ETM.MirrorMass * (2 * np.pi * self.freq) ** 2) - - -######################### -# seismic -######################### - -def Seismic_constructor(direction): - """Seismic noise for a single direction - - """ - if direction == 'horiz': - label = 'Horizontal' - color = 'xkcd:muted blue' - elif direction == 'vert': - label = 'Vertical' - color = 'xkcd:brick red' - - class SeismicDirection(nb.Noise): - name = label - style = dict( - label=label, - color=color, - ) - - @nb.precomp(sustf=suspension.precomp_suspension) - def calc(self, sustf): - nt, nr = noise.seismic.platform_motion(self.freq, self.ifo) - n = noise.seismic.seismic_suspension_filtered(sustf, nt, direction) - return n * 4 - - return SeismicDirection - - -class Seismic(nb.Budget): - """Seismic - - """ - style = dict( - label='Seismic', - color='#855700', - ) - - noises = [ - Seismic_constructor('vert'), - Seismic_constructor('horiz'), - ] - - -######################### -# Newtonian -######################### - -class Newtonian(nb.Noise): - """Newtonian Gravity - - """ - style = dict( - label='Newtonian Gravity', - color='#15b01a', - ) - - def calc(self): - n = noise.newtonian.gravg(self.freq, self.ifo.Seismic) - return n * 4 - - -class NewtonianRayleigh(nb.Noise): - """Newtonian Gravity, Rayleigh waves - - """ - style = dict( - label='Newtonian (Rayleigh waves)', - color='#1b2431', - ) - - def calc(self): - n = noise.newtonian.gravg_rayleigh(self.freq, self.ifo.Seismic) - return n * 2 - - -class NewtonianBody(nb.Noise): - """Newtonian Gravity, body waves - - """ - style = dict( - label='Newtonian (body waves)', - color='#85a3b2', - ) - - def calc(self): - np = noise.newtonian.gravg_pwave(self.freq, self.ifo.Seismic) - ns = noise.newtonian.gravg_swave(self.freq, self.ifo.Seismic) - return (np + ns) * 4 - - -class NewtonianInfrasound(nb.Noise): - """Newtonian Gravity, infrasound - - """ - style = dict( - label='Newtonian (infrasound)', - color='#ffa62b', - ) - - def calc(self): - n = noise.newtonian.atmois(self.freq, self.ifo.Atmospheric, self.ifo.Seismic) - return n * 2 - - -######################### -# suspension thermal -######################### - -def SuspensionThermal_constructor(stage_num, direction): - """Suspension thermal for a single stage in one direction - - """ - if stage_num == 0: - stage_name = 'Top' - color = 'xkcd:orangeish' - elif stage_num == 1: - stage_name = 'UIM' - color = 'xkcd:mustard' - elif stage_num == 2: - stage_name = 'PUM' - color = 'xkcd:turquoise' - elif stage_num == 3: - stage_name = 'Test mass' - color = 'xkcd:bright purple' - - if direction == 'horiz': - name0 = 'Horiz' + stage_name - label = 'Horiz. ' + stage_name - linestyle = '-' - elif direction == 'vert': - name0 = 'Vert' + stage_name - label = 'Vert. ' + stage_name - linestyle = '--' - - class SuspensionThermalStage(nb.Noise): - name = name0 - style = dict( - label=label, - color=color, - linestyle=linestyle, - alpha=0.7, - ) - - @nb.precomp(sustf=suspension.precomp_suspension) - def calc(self, sustf): - n = noise.suspensionthermal.susptherm_stage( - self.freq, self.ifo.Suspension, sustf, stage_num, direction) - return abs(n) * 4 - - return SuspensionThermalStage - - -class SuspensionThermal(nb.Budget): - """Suspension Thermal - - """ - - name = 'SuspensionThermal' - - style = dict( - label='Suspension Thermal', - color='#0d75f8', - ) - - noises = [ - SuspensionThermal_constructor(0, 'horiz'), - SuspensionThermal_constructor(1, 'horiz'), - SuspensionThermal_constructor(2, 'horiz'), - SuspensionThermal_constructor(3, 'horiz'), - SuspensionThermal_constructor(0, 'vert'), - SuspensionThermal_constructor(1, 'vert'), - SuspensionThermal_constructor(2, 'vert'), - ] - - -######################### -# coating thermal -######################### - -class CoatingBrownian(nb.Noise): - """Coating Brownian - - """ - style = dict( - label='Coating Brownian', - color='#fe0002', - ) - - def calc(self): - ITM = mirror_struct(self.ifo, 'ITM') - ETM = mirror_struct(self.ifo, 'ETM') - cavity = arm_cavity(self.ifo) - wavelength = self.ifo.Laser.Wavelength - nITM = noise.coatingthermal.coating_brownian( - self.freq, ITM, wavelength, cavity.wBeam_ITM - ) - nETM = noise.coatingthermal.coating_brownian( - self.freq, ETM, wavelength, cavity.wBeam_ETM - ) - return (nITM + nETM) * 2 - - -class CoatingThermoOptic(nb.Noise): - """Coating Thermo-Optic - - """ - style = dict( - label='Coating Thermo-Optic', - color='#02ccfe', - linestyle='--', - ) - - def calc(self): - wavelength = self.ifo.Laser.Wavelength - materials = self.ifo.Materials - ITM = mirror_struct(self.ifo, 'ITM') - ETM = mirror_struct(self.ifo, 'ETM') - cavity = arm_cavity(self.ifo) - nITM, junk1, junk2, junk3 = noise.coatingthermal.coating_thermooptic( - self.freq, ITM, wavelength, cavity.wBeam_ITM, - ) - nETM, junk1, junk2, junk3 = noise.coatingthermal.coating_thermooptic( - self.freq, ETM, wavelength, cavity.wBeam_ETM, - ) - return (nITM + nETM) * 2 - - -######################### -# substrate thermal -######################### - -class ITMThermoRefractive(nb.Noise): - """ITM Thermo-Refractive - - """ - style = dict( - label='ITM Thermo-Refractive', - color='#448ee4', - linestyle='--', - ) - - def calc(self): - power = ifo_power(self.ifo) - gPhase = power.finesse * 2/np.pi - cavity = arm_cavity(self.ifo) - n = noise.substratethermal.substrate_thermorefractive( - self.freq, self.ifo.Materials, cavity.wBeam_ITM) - return n * 2 / gPhase**2 - - -class SubstrateBrownian(nb.Noise): - """Substrate Brownian - - """ - style = dict( - label='Substrate Brownian', - color='#fb7d07', - linestyle='--', - ) - - def calc(self): - cavity = arm_cavity(self.ifo) - nITM = noise.substratethermal.substrate_brownian( - self.freq, self.ifo.Materials, cavity.wBeam_ITM) - nETM = noise.substratethermal.substrate_brownian( - self.freq, self.ifo.Materials, cavity.wBeam_ETM) - return (nITM + nETM) * 2 - - -class SubstrateThermoElastic(nb.Noise): - """Substrate Thermo-Elastic - - """ - style = dict( - label='Substrate Thermo-Elastic', - color='#f5bf03', - linestyle='--', - ) - - def calc(self): - cavity = arm_cavity(self.ifo) - nITM = noise.substratethermal.substrate_thermoelastic( - self.freq, self.ifo.Materials, cavity.wBeam_ITM) - nETM = noise.substratethermal.substrate_thermoelastic( - self.freq, self.ifo.Materials, cavity.wBeam_ETM) - return (nITM + nETM) * 2 - - -######################### -# residual gas -######################### - -# FIXME HACK: it's unclear if a phase noise in the arms like -# the excess gas noise should get the same dhdL strain -# calibration as the other displacement noises. However, we -# would like to use the one Strain calibration for all noises, -# so we need to divide by the sinc_sqr here to undo the -# application of the dhdl in the Strain calibration. But this -# is ultimately a superfluous extra calculation with the only -# to provide some simplication in the Budget definition, so -# should be re-evaluated at some point. - -RESGAS_STYLES = dict( - H2 = dict( - label='H$_2$', - color='xkcd:red orange', - ), - - N2 = dict( - label='N$_2$', - color='xkcd:emerald', - ), - - H2O = dict( - label='H$_2$O', - color='xkcd:water blue', - ), - - O2 = dict( - label='O$_2$', - color='xkcd:grey', - ), -) - - -def ResidualGasScattering_constructor(species_name): - """Residual gas scattering for a single species - - """ - - class GasScatteringSpecies(nb.Noise): - name = 'Scattering' + species_name - style = dict( - label=RESGAS_STYLES[species_name]['label'] + ' scattering', - color=RESGAS_STYLES[species_name]['color'], - linestyle='-', - ) - - def calc(self): - cavity = arm_cavity(self.ifo) - species = self.ifo.Infrastructure.ResidualGas[species_name] - n = noise.residualgas.residual_gas_scattering_arm( - self.freq, self.ifo, cavity, species) - dhdl_sqr, sinc_sqr = dhdl(self.freq, self.ifo.Infrastructure.Length) - return n * 2 / sinc_sqr - - return GasScatteringSpecies - - -def ResidualGasDamping_constructor(species_name): - """Reisidual gas damping for a single species - - """ - - class GasDampingSpecies(nb.Noise): - name = 'Damping' + species_name - style = dict( - label=RESGAS_STYLES[species_name]['label'] + ' damping', - color=RESGAS_STYLES[species_name]['color'], - linestyle='--', - ) - - @nb.precomp(sustf=suspension.precomp_suspension) - def calc(self, sustf): - rg = self.ifo.Infrastructure.ResidualGas - species = rg[species_name] - squeezed_film = rg.get('SqueezedFilm', Struct()) - - if squeezed_film is None: - raise ValueError('Must specify either excess damping or a gap') - - # Calculate squeezed film for ETM and ITM seperately if either is given - # explicitly. If only one is given, it is not computed for the other one. - if ('ETM' in squeezed_film) or ('ITM' in squeezed_film): - squeezed_film_ETM = squeezed_film.get('ETM', Struct()) - squeezed_film_ITM = squeezed_film.get('ITM', Struct()) - n_ETM = noise.residualgas.residual_gas_damping_test_mass( - self.freq, self.ifo, species, sustf, squeezed_film_ETM) - n_ITM = noise.residualgas.residual_gas_damping_test_mass( - self.freq, self.ifo, species, sustf, squeezed_film_ITM) - n = 2 * (n_ETM + n_ITM) - - # Otherwise the same calculation is used for both. - else: - n = 4 * noise.residualgas.residual_gas_damping_test_mass( - self.freq, self.ifo, species, sustf, squeezed_film) - - return n - - return GasDampingSpecies - - -class ExcessGas(nb.Budget): - """Excess Gas - - """ - style = dict( - label='Residual Gas', - color='#add00d', - linestyle='-', - ) - - noises = [ - ResidualGasScattering_constructor('H2'), - ResidualGasScattering_constructor('N2'), - ResidualGasScattering_constructor('H2O'), - ResidualGasScattering_constructor('O2'), - ResidualGasDamping_constructor('H2'), - ResidualGasDamping_constructor('N2'), - ResidualGasDamping_constructor('H2O'), - ResidualGasDamping_constructor('O2'), - ] diff --git a/gwinc/noise/coatingthermal.py b/gwinc/noise/coatingthermal.py index a53cc3b59034c0a9e20756021b9b55828b7b5e48..03e746df10f7aa408dec6d62688fe79077cb71c9 100644 --- a/gwinc/noise/coatingthermal.py +++ b/gwinc/noise/coatingthermal.py @@ -4,10 +4,61 @@ from __future__ import division, print_function import numpy as np from numpy import pi, exp, real, imag, sqrt, sin, cos, sinh, cosh, ceil, log +import copy from .. import const from ..const import BESSEL_ZEROS as zeta from ..const import J0M as j0m +from .. import nb +from ..ifo.noises import arm_cavity + + +class CoatingBrownian(nb.Noise): + """Coating Brownian + + """ + style = dict( + label='Coating Brownian', + color='#fe0002', + ) + + def calc(self): + ITM = mirror_struct(self.ifo, 'ITM') + ETM = mirror_struct(self.ifo, 'ETM') + cavity = arm_cavity(self.ifo) + wavelength = self.ifo.Laser.Wavelength + nITM = coating_brownian( + self.freq, ITM, wavelength, cavity.wBeam_ITM + ) + nETM = coating_brownian( + self.freq, ETM, wavelength, cavity.wBeam_ETM + ) + return (nITM + nETM) * 2 + + +class CoatingThermoOptic(nb.Noise): + """Coating Thermo-Optic + + """ + style = dict( + label='Coating Thermo-Optic', + color='#02ccfe', + linestyle='--', + ) + + def calc(self): + wavelength = self.ifo.Laser.Wavelength + materials = self.ifo.Materials + ITM = mirror_struct(self.ifo, 'ITM') + ETM = mirror_struct(self.ifo, 'ETM') + cavity = arm_cavity(self.ifo) + nITM, _, _, _ = coating_thermooptic( + self.freq, ITM, wavelength, cavity.wBeam_ITM, + ) + nETM, _, _, _ = coating_thermooptic( + self.freq, ETM, wavelength, cavity.wBeam_ETM, + ) + return (nITM + nETM) * 2 def coating_brownian(f, mirror, wavelength, wBeam, power=None): @@ -1028,3 +1079,28 @@ def interpretLossAngles(coat): lossBlown = lossSlown = lambda f: coat.Philown return lossBhighn, lossShighn, lossBlown, lossSlown + + +def mirror_struct(ifo, tm): + """Create a "mirror" Struct for a LIGO core optic + + This is a copy of the ifo.Materials Struct, containing Substrate + and Coating sub-Structs, as well as some basic geometrical + properties of the optic. + + """ + # NOTE: we deepcopy this struct since we'll be modifying it (and + # it would otherwise be stored by reference) + mirror = copy.deepcopy(ifo.Materials) + optic = ifo.Optics.get(tm) + if 'CoatLayerOpticalThickness' in optic: + mirror.Coating.dOpt = optic['CoatLayerOpticalThickness'] + else: + T = optic.Transmittance + dL = optic.CoatingThicknessLown + dCap = optic.CoatingThicknessCap + mirror.Coating.dOpt = getCoatDopt(mirror, T, dL, dCap=dCap) + mirror.update(optic) + mirror.MassVolume = pi * mirror.MassRadius**2 * mirror.MassThickness + mirror.MirrorMass = mirror.MassVolume * mirror.Substrate.MassDensity + return mirror diff --git a/gwinc/noise/newtonian.py b/gwinc/noise/newtonian.py index db17432f0c292381f09196e921bdb4d807165ac5..2e4688465e75e514549ab951e30f9b5db8717bd7 100644 --- a/gwinc/noise/newtonian.py +++ b/gwinc/noise/newtonian.py @@ -9,6 +9,64 @@ import scipy.special as sp from .seismic import seismic_ground_NLNM from .. import const +from .. import nb + + +class Newtonian(nb.Noise): + """Newtonian Gravity + + """ + style = dict( + label='Newtonian Gravity', + color='#15b01a', + ) + + def calc(self): + n = gravg(self.freq, self.ifo.Seismic) + return n * 4 + + +class Rayleigh(nb.Noise): + """Newtonian Gravity, Rayleigh waves + + """ + style = dict( + label='Rayleigh waves', + color='#1b2431', + ) + + def calc(self): + n = gravg_rayleigh(self.freq, self.ifo.Seismic) + return n * 2 + + +class Body(nb.Noise): + """Newtonian Gravity, body waves + + """ + style = dict( + label='Body waves', + color='#85a3b2', + ) + + def calc(self): + np = gravg_pwave(self.freq, self.ifo.Seismic) + ns = gravg_swave(self.freq, self.ifo.Seismic) + return (np + ns) * 4 + + +class Infrasound(nb.Noise): + """Newtonian Gravity, infrasound + + """ + style = dict( + label='Infrasound)', + color='#ffa62b', + ) + + def calc(self): + n = atmois(self.freq, self.ifo.Atmospheric, self.ifo.Seismic) + return n * 2 def gravg(f, seismic): diff --git a/gwinc/noise/quantum.py b/gwinc/noise/quantum.py index 1b4349327cc7717ee26f06d0bc00f7d91edba3ee..1fcdae1b6b56ee666f32b11432eac67593ae9015 100644 --- a/gwinc/noise/quantum.py +++ b/gwinc/noise/quantum.py @@ -11,10 +11,10 @@ from .. import logger from .. import const from ..struct import Struct from .. import nb -from .. import suspension +from ..suspension import precomp_suspension -@nb.precomp(sustf=suspension.precomp_suspension) +@nb.precomp(sustf=precomp_suspension) def precomp_quantum(f, ifo, sustf): from ..ifo import noises pc = Struct() @@ -46,6 +46,137 @@ def precomp_quantum(f, ifo, sustf): return pc + +class QuantumVacuum(nb.Noise): + """Quantum Vacuum + + """ + style = dict( + label='Quantum Vacuum', + color='#ad03de', + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + total = np.zeros_like(quantum.ASvac) + for nn in quantum.values(): + total += nn + return total + + +class AS(nb.Noise): + """Quantum vacuum from the AS port + + """ + style = dict( + label='AS Port Vacuum', + color='xkcd:emerald green' + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + return quantum.ASvac + + +class Arm(nb.Noise): + """Quantum vacuum due to arm cavity loss + + """ + style = dict( + label='Arm Loss', + color='xkcd:orange brown' + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + return quantum.Arm + + +class SEC(nb.Noise): + """Quantum vacuum due to SEC loss + + """ + style = dict( + label='SEC Loss', + color='xkcd:cerulean' + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + return quantum.SEC + + +class FilterCavity(nb.Noise): + """Quantum vacuum due to filter cavity loss + + """ + style = dict( + label='Filter Cavity Loss', + color='xkcd:goldenrod' + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + return quantum.FC + + +class Injection(nb.Noise): + """Quantum vacuum due to injection loss + + """ + style = dict( + label='Injection Loss', + color='xkcd:fuchsia' + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + return quantum.Injection + + +class Readout(nb.Noise): + """Quantum vacuum due to readout loss + + """ + style = dict( + label='Readout Loss', + color='xkcd:mahogany' + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + return quantum.PD + + +class QuadraturePhase(nb.Noise): + """Quantum vacuum noise due to quadrature phase noise + """ + style = dict( + label='Quadrature Phase', + color='xkcd:slate' + ) + + @nb.precomp(quantum=precomp_quantum) + def calc(self, quantum): + return quantum.Phase + + +class StandardQuantumLimit(nb.Noise): + """Standard Quantum Limit + + """ + style = dict( + label="Standard Quantum Limit", + color="#000000", + linestyle=":", + ) + + def calc(self): + from .coatingthermal import mirror_struct + ETM = mirror_struct(self.ifo, 'ETM') + return 8 * const.hbar / (ETM.MirrorMass * (2 * np.pi * self.freq) ** 2) + + def sqzOptimalSqueezeAngle(Mifo, eta): vHD = np.array([[sin(eta), cos(eta)]]) H = getProdTF(vHD, Mifo)[0] diff --git a/gwinc/noise/residualgas.py b/gwinc/noise/residualgas.py index df00e093933fd756282b314eef0504388c78ca09..39790eff92e43aa6a30884c0a09ccfe24762d3bc 100644 --- a/gwinc/noise/residualgas.py +++ b/gwinc/noise/residualgas.py @@ -6,6 +6,132 @@ from numpy import sqrt, log, pi from .. import const from .. import Struct +from .. import nb +from ..ifo.noises import dhdl, arm_cavity +from ..suspension import precomp_suspension + + +RESGAS_STYLES = dict( + H2 = dict( + label='H$_2$', + color='xkcd:red orange', + ), + + N2 = dict( + label='N$_2$', + color='xkcd:emerald', + ), + + H2O = dict( + label='H$_2$O', + color='xkcd:water blue', + ), + + O2 = dict( + label='O$_2$', + color='xkcd:grey', + ), +) + + +# FIXME HACK: it's unclear if a phase noise in the arms like +# the excess gas noise should get the same dhdL strain +# calibration as the other displacement noises. However, we +# would like to use the one Strain calibration for all noises, +# so we need to divide by the sinc_sqr here to undo the +# application of the dhdl in the Strain calibration. But this +# is ultimately a superfluous extra calculation with the only +# to provide some simplication in the Budget definition, so +# should be re-evaluated at some point. + + +def ResidualGasScattering_constructor(species_name): + """Residual gas scattering for a single species + + """ + + class GasScatteringSpecies(nb.Noise): + name = 'Scattering' + species_name + style = dict( + label=RESGAS_STYLES[species_name]['label'] + ' scattering', + color=RESGAS_STYLES[species_name]['color'], + linestyle='-', + ) + + def calc(self): + cavity = arm_cavity(self.ifo) + species = self.ifo.Infrastructure.ResidualGas[species_name] + n = residual_gas_scattering_arm( + self.freq, self.ifo, cavity, species) + dhdl_sqr, sinc_sqr = dhdl(self.freq, self.ifo.Infrastructure.Length) + return n * 2 / sinc_sqr + + return GasScatteringSpecies + + +def ResidualGasDamping_constructor(species_name): + """Reisidual gas damping for a single species + + """ + + class GasDampingSpecies(nb.Noise): + name = 'Damping' + species_name + style = dict( + label=RESGAS_STYLES[species_name]['label'] + ' damping', + color=RESGAS_STYLES[species_name]['color'], + linestyle='--', + ) + + @nb.precomp(sustf=precomp_suspension) + def calc(self, sustf): + rg = self.ifo.Infrastructure.ResidualGas + species = rg[species_name] + squeezed_film = rg.get('SqueezedFilm', Struct()) + + if squeezed_film is None: + raise ValueError('Must specify either excess damping or a gap') + + # Calculate squeezed film for ETM and ITM seperately if either is given + # explicitly. If only one is given, it is not computed for the other one. + if ('ETM' in squeezed_film) or ('ITM' in squeezed_film): + squeezed_film_ETM = squeezed_film.get('ETM', Struct()) + squeezed_film_ITM = squeezed_film.get('ITM', Struct()) + n_ETM = residual_gas_damping_test_mass( + self.freq, self.ifo, species, sustf, squeezed_film_ETM) + n_ITM = residual_gas_damping_test_mass( + self.freq, self.ifo, species, sustf, squeezed_film_ITM) + n = 2 * (n_ETM + n_ITM) + + # Otherwise the same calculation is used for both. + else: + n = 4 * residual_gas_damping_test_mass( + self.freq, self.ifo, species, sustf, squeezed_film) + + return n + + return GasDampingSpecies + + +class ResidualGas(nb.Budget): + """Residual Gas + + """ + style = dict( + label='Residual Gas', + color='#add00d', + linestyle='-', + ) + + noises = [ + ResidualGasScattering_constructor('H2'), + ResidualGasScattering_constructor('N2'), + ResidualGasScattering_constructor('H2O'), + ResidualGasScattering_constructor('O2'), + ResidualGasDamping_constructor('H2'), + ResidualGasDamping_constructor('N2'), + ResidualGasDamping_constructor('H2O'), + ResidualGasDamping_constructor('O2'), + ] def residual_gas_scattering_arm(f, ifo, cavity, species): diff --git a/gwinc/noise/seismic.py b/gwinc/noise/seismic.py index 296e51ddf0045bb0ec3395b531c43b90e41d587f..ab6b07e3524b3e41a9e70171fcd8c8c76165c4ac 100644 --- a/gwinc/noise/seismic.py +++ b/gwinc/noise/seismic.py @@ -5,6 +5,51 @@ from __future__ import division import numpy as np from scipy.interpolate import PchipInterpolator as interp1d +from .. import nb +from ..suspension import precomp_suspension + + +def Seismic_constructor(direction): + """Seismic noise for a single direction + + """ + if direction == 'horiz': + label = 'Horizontal' + color = 'xkcd:muted blue' + elif direction == 'vert': + label = 'Vertical' + color = 'xkcd:brick red' + + class SeismicDirection(nb.Noise): + name = label + style = dict( + label=label, + color=color, + ) + + @nb.precomp(sustf=precomp_suspension) + def calc(self, sustf): + nt, nr = platform_motion(self.freq, self.ifo) + n = seismic_suspension_filtered(sustf, nt, direction) + return n * 4 + + return SeismicDirection + + +class Seismic(nb.Budget): + """Seismic + + """ + style = dict( + label='Seismic', + color='#855700', + ) + + noises = [ + Seismic_constructor('vert'), + Seismic_constructor('horiz'), + ] + def seismic_suspension_filtered(sustf, in_trans, direction): """Seismic displacement noise for single suspended test mass. diff --git a/gwinc/noise/substratethermal.py b/gwinc/noise/substratethermal.py index d6ec92ed77a2f4a2b395e112008fb090aa8065bc..fd9fa94330d86c40504c0d640a6ea2eb4c950345 100644 --- a/gwinc/noise/substratethermal.py +++ b/gwinc/noise/substratethermal.py @@ -10,6 +10,65 @@ import scipy.integrate from .. import const from ..const import BESSEL_ZEROS as zeta from ..const import J0M as j0m +from .. import nb +from ..ifo.noises import arm_cavity, ifo_power + + +class ITMThermoRefractive(nb.Noise): + """ITM Thermo-Refractive + + """ + style = dict( + label='ITM Thermo-Refractive', + color='#448ee4', + linestyle='--', + ) + + def calc(self): + power = ifo_power(self.ifo) + gPhase = power.finesse * 2/np.pi + cavity = arm_cavity(self.ifo) + n = substrate_thermorefractive( + self.freq, self.ifo.Materials, cavity.wBeam_ITM) + return n * 2 / gPhase**2 + + +class SubstrateBrownian(nb.Noise): + """Substrate Brownian + + """ + style = dict( + label='Substrate Brownian', + color='#fb7d07', + linestyle='--', + ) + + def calc(self): + cavity = arm_cavity(self.ifo) + nITM = substrate_brownian( + self.freq, self.ifo.Materials, cavity.wBeam_ITM) + nETM = substrate_brownian( + self.freq, self.ifo.Materials, cavity.wBeam_ETM) + return (nITM + nETM) * 2 + + +class SubstrateThermoElastic(nb.Noise): + """Substrate Thermo-Elastic + + """ + style = dict( + label='Substrate Thermo-Elastic', + color='#f5bf03', + linestyle='--', + ) + + def calc(self): + cavity = arm_cavity(self.ifo) + nITM = substrate_thermoelastic( + self.freq, self.ifo.Materials, cavity.wBeam_ITM) + nETM = substrate_thermoelastic( + self.freq, self.ifo.Materials, cavity.wBeam_ETM) + return (nITM + nETM) * 2 def substrate_thermorefractive(f, materials, wBeam, exact=False): diff --git a/gwinc/noise/suspensionthermal.py b/gwinc/noise/suspensionthermal.py index d73cd69b8257c5c8e11a61c9ec61c523581319f3..6184dd7bd8a2f33ae765df2d9cce59e2d51118dd 100644 --- a/gwinc/noise/suspensionthermal.py +++ b/gwinc/noise/suspensionthermal.py @@ -6,7 +6,76 @@ import numpy as np from numpy import pi, imag from ..const import kB -from ..suspension import getJointParams +from ..suspension import getJointParams, precomp_suspension +from .. import nb + + + +def SuspensionThermal_constructor(stage_num, direction): + """Suspension thermal for a single stage in one direction + + """ + if stage_num == 0: + stage_name = 'Top' + color = 'xkcd:orangeish' + elif stage_num == 1: + stage_name = 'UIM' + color = 'xkcd:mustard' + elif stage_num == 2: + stage_name = 'PUM' + color = 'xkcd:turquoise' + elif stage_num == 3: + stage_name = 'Test mass' + color = 'xkcd:bright purple' + + if direction == 'horiz': + name0 = 'Horiz' + stage_name + label = 'Horiz. ' + stage_name + linestyle = '-' + elif direction == 'vert': + name0 = 'Vert' + stage_name + label = 'Vert. ' + stage_name + linestyle = '--' + + class SuspensionThermalStage(nb.Noise): + name = name0 + style = dict( + label=label, + color=color, + linestyle=linestyle, + alpha=0.7, + ) + + @nb.precomp(sustf=precomp_suspension) + def calc(self, sustf): + n = susptherm_stage( + self.freq, self.ifo.Suspension, sustf, stage_num, direction) + return abs(n) * 4 + + return SuspensionThermalStage + + +class SuspensionThermal(nb.Budget): + """Suspension Thermal + + """ + + name = 'SuspensionThermal' + + style = dict( + label='Suspension Thermal', + color='#0d75f8', + ) + + noises = [ + SuspensionThermal_constructor(0, 'horiz'), + SuspensionThermal_constructor(1, 'horiz'), + SuspensionThermal_constructor(2, 'horiz'), + SuspensionThermal_constructor(3, 'horiz'), + SuspensionThermal_constructor(0, 'vert'), + SuspensionThermal_constructor(1, 'vert'), + SuspensionThermal_constructor(2, 'vert'), + ] def getJointTempSusceptibility(sus, sustf, stage_num, isUpper, direction):