Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • wenxuan.jia/pygwinc
  • sean-leavey/pygwinc
  • sebastian.steinlechner/pygwinc
  • nicholas.demos/pygwinc
  • chris.whittle/pygwinc
  • raymond.robie/pygwinc
  • mateusz.bawaj/pygwinc
  • anchal.gupta/pygwinc
  • 40m/pygwinc
  • evan.hall/pygwinc
  • kevin.kuns/pygwinc
  • geoffrey-lovelace/pygwinc
  • brittany.kamai/pygwinc
  • daniel-brown/pygwinc
  • lee-mcculler/pygwinc
  • jameson.rollins/pygwinc
  • gwinc/pygwinc
17 results
Show changes
Commits on Source (53)
Showing
with 498 additions and 279 deletions
......@@ -6,6 +6,10 @@ test_results*/
gwinc/test/cache
test/*/*.h5
gwinc.egg-info/
.eggs/
gwinc/_version.py
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
......
......@@ -3,6 +3,7 @@ stages:
- test
- review
- deploy
- release
# have to specify this so that all jobs execute for all commits
# including merge requests
......@@ -10,6 +11,7 @@ workflow:
rules:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH
- if: $CI_COMMIT_TAG
variables:
GIT_STRATEGY: clone
......@@ -25,7 +27,7 @@ gwinc/base:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- |
cat <<EOF > Dockerfile
FROM igwn/base:buster
FROM igwn/base:bullseye
RUN apt-get update -qq
RUN apt-get -y install --no-install-recommends git python3 python3-gitlab python3-setuptools-scm python3-yaml python3-scipy python3-matplotlib python3-pypdf2 python3-h5py python3-inspiral-range python3-lalsimulation
EOF
......@@ -122,3 +124,20 @@ pages:
when: always
paths:
- public
# make pypi release on tag creation
pypi_release:
stage: release
rules:
- if: $CI_PROJECT_NAMESPACE != "gwinc"
when: never
- if: $CI_COMMIT_TAG =~ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/
image: python:latest
script:
- echo "pypi release for $CI_COMMIT_TAG"
- pip install twine
- python setup.py sdist bdist_wheel
- TWINE_USERNAME=__token__ TWINE_PASSWORD=${PYPI_TOKEN} twine upload dist/*
artifacts:
paths:
- dist/*
......@@ -153,8 +153,8 @@ is available in the `budget.ifo` attribute.
The budget `run()` method calculates all budget noises and the noise
total and returns a `BudgetTrace` object with `freq`, `psd`, and `asd`
properties. The budget sub-traces are available through a dictionary
(`trace['QuantumVacuum']`) interface and via attributes
(`trace.QuantumVacumm`).
(`trace['Quantum']`) interface and via attributes
(`trace.Quantum`).
The budget `freq` and `ifo` attributes can be updated at run time by
passing them as keyword arguments to the `run()` method:
......@@ -438,21 +438,21 @@ and extract the noise data the output traces dictionary:
```python
budget = load_budget('/path/to/MyBudget', freq)
trace = budget.run()
quantum_trace = trace['QuantumVacuum']
quantum_trace = trace['Quantum']
```
You can also calculate the final calibrated output noise for just a
single term using the Budget `calc_noise()` method:
```python
data = budget.calc_noise('QuantumVacuum')
data = budget.calc_noise('Quantum')
```
You can also calculate a noise at it's source, without applying any
calibrations, by using the Budget `__getitem__` interface to extract
the specific Noise BudgetItem for the noise you're interested in, and
running it's `calc()` method directly:
running it's `calc_trace()` method directly:
```python
data = budget['QuantumVacuum'].calc()
data = budget['Quantum'].calc_trace()
```
......
......@@ -71,7 +71,7 @@ def tpath_preclear(request):
def tpath(request):
"""
Fixture that takes the value of the special test-specific folder for test
run data and plots. Usually the <folder of the test>/tresults/test_name/
run data and plots. Usually the <folder of the test>/test_results/test_name/
"""
tpath_raw = tpath_raw_make(request)
......@@ -84,7 +84,7 @@ def tpath(request):
def tpath_join(request):
"""
Fixture that joins subpaths to the value of the special test-specific folder for test
run data and plots. Usually the <folder of the test>/tresults/test_name/.
run data and plots. Usually the <folder of the test>/test_results/test_name/.
This function should be use like test_thing.save(tpath_join('output_file.png'))
"""
......@@ -236,7 +236,7 @@ def pprint(request, tpath_join):
def tpath_raw_make(request):
if isinstance(request.node, pytest.Function):
return relfile_test(request.node.function.__code__.co_filename, request, 'tresults')
return relfile_test(request.node.function.__code__.co_filename, request, 'test_results')
raise RuntimeError("TPath currently only works for functions")
......
......@@ -308,10 +308,17 @@ You may interact with the plot using the 'plt' functions, e.g.:
In [.]: plt.title("foo")
In [.]: plt.savefig("foo.pdf")
"""
from IPython.core import getipython
from IPython.terminal.embed import InteractiveShellEmbed
if subtitle:
plot_style['title'] += '\n' + subtitle
ipshell = InteractiveShellEmbed(
# deal with breaking change in ipython embedded mode
# https://github.com/ipython/ipython/issues/13966
if getipython.get_ipython() is None:
embed = InteractiveShellEmbed.instance
else:
embed = InteractiveShellEmbed
ipshell = embed(
banner1=banner,
user_ns={
'ifo': ifo,
......
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
from gwinc.ifo.noises import Strain
class Quantum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
noise.quantum.AS,
noise.quantum.Arm,
noise.quantum.SEC,
noise.quantum.FilterCavity,
noise.quantum.Injection,
noise.quantum.Readout,
noise.quantum.QuadraturePhase,
]
import gwinc.ifo.noises as calibrations
class Aplus(nb.Budget):
......@@ -29,7 +9,7 @@ class Aplus(nb.Budget):
name = 'A+'
noises = [
Quantum,
noise.quantum.Quantum,
noise.seismic.Seismic,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
......@@ -41,7 +21,29 @@ class Aplus(nb.Budget):
]
calibrations = [
Strain,
calibrations.Strain,
]
plot_style = PLOT_STYLE
class Displacement(Aplus):
calibrations = []
class Acceleration(Aplus):
calibrations = [
calibrations.Acceleration,
]
class Velocity(Aplus):
calibrations = [
calibrations.Velocity,
]
class Force(Aplus):
calibrations = [
calibrations.Force,
]
# GWINC aLIGO interferometer parameters
# GWINC A+ interferometer parameters
#
# parameters for quad pendulum suspension updated 3rd May 2006, NAR
# References:
......@@ -96,8 +96,29 @@ Seismic:
Beta: 0.8 # quiet times beta: 0.35-0.60
# noisy times beta: 0.15-1.4
Omicron: 1 # Feedforward cancellation factor
pWaveSpeed: 600 # m/s
sWaveSpeed: 300 # m/s
TestMassHeight: 1.5 # m
RayleighWaveSpeed: 250 # m/s
pWaveLevel: 45 # Multiple of the Peterson NLNM amplitude
sWaveLevel: 45 # Multiple of the Peterson NLNM amplitude
PlatformMotion: 'BSC'
Atmospheric:
AirPressure: 101325 # Pa
AirDensity: 1.225 # kg/m**3
AirKinematicViscosity: 1.8e-5 # m**2/s
AdiabaticIndex: 1.4 #
SoundSpeed: 344 # m/s
WindSpeed: 10 # m/s; typical value
Temperature: 300 # K
TempStructConst: 0.2 # K**2/m**(2/3);
TempStructExp: 0.667 #
TurbOuterScale: 100 # m
# TurbEnergyDissRate: 0.01 # m**2/s**3
KolmEnergy1m: 1 # Kolmogorov energy spectrum at 1/m [m**2/s**2]
Suspension:
Type: 'Quad'
......@@ -288,8 +309,8 @@ Optics:
Tunephase: 0.0 # SEC tuning
Curvature: # ROC
ITM: 1970
ETM: 2192
ITM: 1940
ETM: 2245
## Squeezer Parameters------------------------------------------------------
# Define the squeezing you want:
......
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
from gwinc.ifo.noises import Strain
class Quantum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
noise.quantum.AS,
noise.quantum.Arm,
noise.quantum.SEC,
noise.quantum.FilterCavity,
noise.quantum.Injection,
noise.quantum.Readout,
noise.quantum.QuadraturePhase,
]
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
noise.newtonian.Rayleigh,
noise.newtonian.Body,
noise.newtonian.Infrasound,
]
import gwinc.ifo.noises as calibrations
class Coating(nb.Budget):
......@@ -84,9 +45,9 @@ class CE1(nb.Budget):
name = 'Cosmic Explorer 1'
noises = [
Quantum,
noise.quantum.Quantum,
noise.seismic.Seismic,
Newtonian,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
Coating,
Substrate,
......@@ -94,7 +55,29 @@ class CE1(nb.Budget):
]
calibrations = [
Strain,
calibrations.Strain,
]
plot_style = PLOT_STYLE
class Displacement(CE1):
calibrations = []
class Acceleration(CE1):
calibrations = [
calibrations.Acceleration,
]
class Velocity(CE1):
calibrations = [
calibrations.Velocity,
]
class Force(CE1):
calibrations = [
calibrations.Force,
]
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
from gwinc.ifo.noises import Strain
class Quantum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
noise.quantum.AS,
noise.quantum.Arm,
noise.quantum.SEC,
noise.quantum.FilterCavity,
noise.quantum.Injection,
noise.quantum.Readout,
noise.quantum.QuadraturePhase,
]
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
noise.newtonian.Rayleigh,
noise.newtonian.Body,
noise.newtonian.Infrasound,
]
import gwinc.ifo.noises as calibrations
class Coating(nb.Budget):
......@@ -84,9 +45,9 @@ class CE2silica(nb.Budget):
name = 'Cosmic Explorer 2 (Silica)'
noises = [
Quantum,
noise.quantum.Quantum,
noise.seismic.Seismic,
Newtonian,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
Coating,
Substrate,
......@@ -94,7 +55,29 @@ class CE2silica(nb.Budget):
]
calibrations = [
Strain,
calibrations.Strain,
]
plot_style = PLOT_STYLE
class Displacement(CE2silica):
calibrations = []
class Acceleration(CE2silica):
calibrations = [
calibrations.Acceleration,
]
class Velocity(CE2silica):
calibrations = [
calibrations.Velocity,
]
class Force(CE2silica):
calibrations = [
calibrations.Force,
]
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
from gwinc.ifo.noises import Strain
class Quantum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
noise.quantum.AS,
noise.quantum.Arm,
noise.quantum.SEC,
noise.quantum.FilterCavity,
noise.quantum.Injection,
noise.quantum.Readout,
noise.quantum.QuadraturePhase,
]
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
noise.newtonian.Rayleigh,
noise.newtonian.Body,
noise.newtonian.Infrasound,
]
import gwinc.ifo.noises as calibrations
class Coating(nb.Budget):
......@@ -85,9 +46,9 @@ class CE2silicon(nb.Budget):
name = 'Cosmic Explorer 2 (Silicon)'
noises = [
Quantum,
noise.quantum.Quantum,
noise.seismic.Seismic,
Newtonian,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
Coating,
Substrate,
......@@ -95,7 +56,33 @@ class CE2silicon(nb.Budget):
]
calibrations = [
Strain,
calibrations.Strain,
]
plot_style = PLOT_STYLE
class Displacement(CE2silicon):
calibrations = []
class Displacement(CE2silicon):
calibrations = []
class Acceleration(CE2silicon):
calibrations = [
calibrations.Acceleration,
]
class Velocity(CE2silicon):
calibrations = [
calibrations.Velocity,
]
class Force(CE2silicon):
calibrations = [
calibrations.Force,
]
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
from gwinc.ifo.noises import Strain
class Quantum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
noise.quantum.AS,
noise.quantum.Arm,
noise.quantum.SEC,
noise.quantum.FilterCavity,
noise.quantum.Injection,
noise.quantum.Readout,
]
import gwinc.ifo.noises as calibrations
class Voyager(nb.Budget):
......@@ -28,7 +9,7 @@ class Voyager(nb.Budget):
name = 'Voyager'
noises = [
Quantum,
noise.quantum.Quantum,
noise.seismic.Seismic,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
......@@ -41,7 +22,29 @@ class Voyager(nb.Budget):
]
calibrations = [
Strain,
calibrations.Strain,
]
plot_style = PLOT_STYLE
class Displacement(Voyager):
calibrations = []
class Acceleration(Voyager):
calibrations = [
calibrations.Acceleration,
]
class Velocity(Voyager):
calibrations = [
calibrations.Velocity,
]
class Force(Voyager):
calibrations = [
calibrations.Force,
]
......@@ -96,10 +96,30 @@ Seismic:
Beta: 0.8 # quiet times beta = 0.35-0.60; noisy times beta = 0.15-1.4
Omicron: 10 # Feedforward cancellation factor
TestMassHeight: 1.5 # m
pWaveSpeed: 600 # m/s
sWaveSpeed: 300 # m/s
RayleighWaveSpeed: 250 # m/s
pWaveLevel: 45 # Multiple of the Peterson NLNM amplitude
sWaveLevel: 45 # Multiple of the Peterson NLNM amplitude
PlatformMotion: '6D'
#darmSeiSusFile: 'CryogenicLIGO/Sensitivity/GWINC/seismic.mat'
Atmospheric:
AirPressure: 101325 # Pa
AirDensity: 1.225 # kg/m**3
AirKinematicViscosity: 1.8e-5 # m**2/s
AdiabaticIndex: 1.4 #
SoundSpeed: 344 # m/s
WindSpeed: 10 # m/s; typical value
Temperature: 300 # K
TempStructConst: 0.2 # K**2/m**(2/3);
TempStructExp: 0.667 #
TurbOuterScale: 100 # m
# TurbEnergyDissRate: 0.01 # m**2/s**3
KolmEnergy1m: 1 # Kolmogorov energy spectrum at 1/m [m**2/s**2]
Suspension:
Type: 'Quad'
VHCoupling:
......@@ -367,6 +387,7 @@ Squeezer:
AmplitudedB: 10 # SQZ amplitude [dB]
InjectionLoss: 0.05 # power loss to sqz
SQZAngle: 0 # SQZ phase [radians]
LOAngleRMS: 10e-3 # quadrature noise [radians]
# Parameters for frequency dependent squeezing
FilterCavity:
......
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
from gwinc.ifo.noises import Strain
import gwinc.ifo.noises as calibrations
class Quantum(nb.Budget):
......@@ -38,7 +38,29 @@ class aLIGO(nb.Budget):
]
calibrations = [
Strain,
calibrations.Strain,
]
plot_style = PLOT_STYLE
class Displacement(aLIGO):
calibrations = []
class Acceleration(aLIGO):
calibrations = [
calibrations.Acceleration,
]
class Velocity(aLIGO):
calibrations = [
calibrations.Velocity,
]
class Force(aLIGO):
calibrations = [
calibrations.Force,
]
......@@ -97,8 +97,28 @@ Seismic:
Beta: 0.8 # quiet times beta: 0.35-0.60
# noisy times beta: 0.15-1.4
Omicron: 1 # Feedforward cancellation factor
pWaveSpeed: 600 # m/s
sWaveSpeed: 300 # m/s
TestMassHeight: 1.5 # m
RayleighWaveSpeed: 250 # m/s
pWaveLevel: 45 # Multiple of the Peterson NLNM amplitude
sWaveLevel: 45 # Multiple of the Peterson NLNM amplitude
Atmospheric:
AirPressure: 101325 # Pa
AirDensity: 1.225 # kg/m**3
AirKinematicViscosity: 1.8e-5 # m**2/s
AdiabaticIndex: 1.4 #
SoundSpeed: 344 # m/s
WindSpeed: 10 # m/s; typical value
Temperature: 300 # K
TempStructConst: 0.2 # K**2/m**(2/3);
TempStructExp: 0.667 #
TurbOuterScale: 100 # m
# TurbEnergyDissRate: 0.01 # m**2/s**3
KolmEnergy1m: 1 # Kolmogorov energy spectrum at 1/m [m**2/s**2]
Suspension:
Type: 'Quad'
......
......@@ -51,7 +51,6 @@ def ifo_power(ifo, PRfixed=True):
"""Compute power on beamsplitter, finesse, and power recycling factor.
"""
pin = ifo.Laser.Power
t1 = sqrt(ifo.Optics.ITM.Transmittance)
r1 = sqrt(1 - ifo.Optics.ITM.Transmittance)
r2 = sqrt(1 - ifo.Optics.ETM.Transmittance)
......@@ -78,8 +77,23 @@ def ifo_power(ifo, PRfixed=True):
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
#allow either the input power or the arm power to be the principle power used
#input power is good for new budgets, while arm power is good for site noise
#budgets
pin = ifo.Laser.get('Power', None)
parm = ifo.Laser.get('ArmPower', None)
if pin is not None:
if parm is not None:
#TODO, make a ConfigError or IfoError?
raise RuntimeError("Cannot specify both Laser.Power and Laser.ArmPower")
pbs = pin * prfactor # BS power from input power
parm = pbs * garm**2 / 2 # arm power from BS power
else:
if parm is None:
#TODO, make a ConfigError or IfoError?
raise RuntimeError("Need to specify either Laser.Power or Laser.ArmPower")
pbs = parm / (garm**2 / 2) # arm power from BS power
pin = pbs / prfactor # BS power from input power
thickness = ifo.Optics.ITM.get('Thickness', ifo.Materials.MassThickness)
asub = 1.3 * 2 * thickness * ifo.Optics.SubstrateAbsorption
......@@ -139,5 +153,21 @@ class Force(nb.Calibration):
"""Calibrate displacement to force
"""
def calc(self):
from ..noise.coatingthermal import mirror_struct
mass = mirror_struct(self.ifo, 'ETM').MirrorMass
return (mass * (2*pi*self.freq)**2)**2
class Acceleration(nb.Calibration):
"""Calibrate displacement to acceleration
"""
def calc(self):
return (2*pi*self.freq)**4
class Velocity(nb.Calibration):
"""Calibrate displacement to velocity
"""
def calc(self):
return (2*pi*self.freq)**2
......@@ -5,6 +5,7 @@ import scipy.interpolate
from . import logger
from .trace import BudgetTrace
from .struct import Struct
def precomp(*precomp_funcs, **precomp_fmaps):
......@@ -74,6 +75,23 @@ def _precomp_recurse_mapping(func, freq, ifo, _precomp):
return kwargs
def list_or_dict_iter(list_or_dict, return_items=False):
"""Iterator over elements of a list or values of a dict or Struct
If return_items is True, the items instead of values of a dict or
struct are returned
"""
if isinstance(list_or_dict, list):
return list_or_dict
elif isinstance(list_or_dict, (dict, Struct)):
if return_items:
return list_or_dict.items()
else:
return list_or_dict.values()
else:
raise ValueError('Input should be either a list, dict, or Struct')
def quadsum(data):
"""Calculate quadrature sum of list of data arrays.
......@@ -339,6 +357,11 @@ class Budget(Noise):
noises = []
"""List of constituent noise classes, or (noise, cal) tuples"""
noises_forward = []
"""List of constituent noise classes, or (noise, cal) tuples.
These are not saved in a sub-budget, but applied into this budget directly.
"""
calibrations = []
"""List of calibrations to be applied to all budget noises (not references)"""
......@@ -382,16 +405,21 @@ class Budget(Noise):
self._noise_cals = collections.defaultdict(set)
# set of all constituent budget noise names
self._budget_noises = set()
# this overlays the class noises with the instance version that has
# been forwarded.
self.noises = self._forward_noises()
# initialize all noise objects
for nc in self.noises:
for nc in list_or_dict_iter(self.noises):
name = self.__init_noise(nc, noises)
if name:
self._budget_noises.add(name)
# initialize common calibrations and add to all budget noises
for cal in self.calibrations:
for cal in list_or_dict_iter(self.calibrations):
self.__add_calibration(cal, self._budget_noises)
# initialize references, without common calibrations
for nc in self.references:
for nc in list_or_dict_iter(self.references):
self.__init_noise(nc, noises)
# error if requested noise is not present
if noises:
......@@ -585,3 +613,77 @@ class Budget(Noise):
return self._make_trace(
psd=total, budget=budget
)
@classmethod
def _forward_noises(cls):
"""Extract noises and calibrations from a list of sub-budgets.
operates recursively through the sub-budgets.
Useful for forwarding a list of noises in a sub-budget into an upper level
budget. This then groups the noises in the upper level budget without
having to analyze the sub-budgets independently.
Parameters
----------
noises : list or dict of Noises
subbudgets : list or dict of Budgets
List of the sub-budgets whose noises will be forwarded.
The noises and their calibrations in subbudgets are added to noises
"""
noises = cls.noises
subbudgets = cls.noises_forward
if not isinstance(subbudgets, (list, dict)):
raise ValueError('Only lists and dicts can be forwarded')
# make a copy to update
if isinstance(noises, list):
noises = list(noises)
# must promote noises
if isinstance(subbudgets, dict):
noises = {
b.__name__: b for b in noises
}
else:
noises = dict(noises)
# must promote the subbudgets
if isinstance(subbudgets, list):
subbudgets = {
b.__name__: b for b in subbudgets
}
for budget in list_or_dict_iter(subbudgets, return_items=True):
if isinstance(subbudgets, dict):
bname, budget = budget
if not isinstance(budget, (tuple, list)):
budget = (budget,)
b = budget[0]
# choosing a tuple type here, the remainder will be
# converted also to tuple, to property overload "+" as concat
cals = tuple(budget[1:])
cals += tuple(b.calibrations)
if isinstance(subbudgets, list):
noises_frwd = [
tuple(n) + cals
if isinstance(n, (tuple, list))
else (n,) + cals
for n in b._forward_noises()
]
noises.extend(noises_frwd)
# this may not work due to assuming b.noises is also a Mapping
elif isinstance(subbudgets, dict):
noises_frwd = {
k: tuple(n) + cals
if isinstance(n, (tuple, list))
else (n,) + cals
for k, n in b._forward_noises().items()
}
noises.update(noises_frwd)
return noises
......@@ -12,20 +12,6 @@ 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
......@@ -60,7 +46,7 @@ class Infrasound(nb.Noise):
"""
style = dict(
label='Infrasound)',
label='Infrasound',
color='#ffa62b',
)
......@@ -69,6 +55,25 @@ class Infrasound(nb.Noise):
return n * 2
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
Rayleigh,
Body,
Infrasound,
]
def gravg(f, seismic):
"""Gravity gradient noise for single test mass
......
......@@ -46,7 +46,6 @@ def precomp_quantum(f, ifo, sustf):
return pc
class QuantumVacuum(nb.Noise):
"""Quantum Vacuum
......@@ -161,6 +160,26 @@ class QuadraturePhase(nb.Noise):
return quantum.Phase
class Quantum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
AS,
Arm,
SEC,
FilterCavity,
Injection,
Readout,
QuadraturePhase,
]
class StandardQuantumLimit(nb.Noise):
"""Standard Quantum Limit
......
......@@ -2,7 +2,9 @@
'''
from __future__ import division
import numpy as np
from numpy import sqrt, log, pi
from scipy.integrate import trapezoid
from .. import const
from .. import Struct
......@@ -60,10 +62,15 @@ def ResidualGasScattering_constructor(species_name):
def calc(self):
cavity = arm_cavity(self.ifo)
Larm_m = self.ifo.Infrastructure.Length
species = self.ifo.Infrastructure.ResidualGas[species_name]
# position along the beamtube starting from vertex corner
tube_pos = np.linspace(0, Larm_m, 100)
# pressure profile is constant by default
pressure_Pa = species.BeamtubePressure * np.ones_like(tube_pos)
n = residual_gas_scattering_arm(
self.freq, self.ifo, cavity, species)
dhdl_sqr, sinc_sqr = dhdl(self.freq, self.ifo.Infrastructure.Length)
self.freq, self.ifo, cavity, species, pressure_Pa, tube_pos)
dhdl_sqr, sinc_sqr = dhdl(self.freq, Larm_m)
return n * 2 / sinc_sqr
return GasScatteringSpecies
......@@ -134,16 +141,16 @@ class ResidualGas(nb.Budget):
]
def residual_gas_scattering_arm(f, ifo, cavity, species):
"""Residual gas noise strain spectrum due to scattering from one arm
Noise caused by the passage of residual gas molecules through the
laser beams in one arm cavity due to scattering.
def residual_gas_scattering_arm(
f, ifo, cavity, species, pressure_Pa, tube_pos):
"""Residual gas scattering from one arm using measured beamtube pressures
:f: frequency array in Hz
:ifo: gwinc IFO structure
:cavity: arm cavity structure
:species: molecular species structure
:pressure_Pa: beamtube pressure profile in Pa
:tubepos_m: vector of positions where pressure is given in m
:returns: arm strain noise power spectrum at :f:
......@@ -151,37 +158,26 @@ def residual_gas_scattering_arm(f, ifo, cavity, species):
E. Zucker, and Stanley E. Whitcomb in their paper Optical
Pathlength Noise in Sensitive Interferometers Due to Residual Gas.
Added to Bench by Zhigang Pan, Summer 2006
Cleaned up by PF, Apr 07
Eliminated numerical integration and substituted first order
expansion of exp, to speed it up.
"""
L = ifo.Infrastructure.Length
# beam profile
ww_m = cavity.w0 * np.sqrt(1 + ((tube_pos - cavity.zBeam_ITM)/cavity.zr)**2)
kT = ifo.Infrastructure.Temp * const.kB
P = species.BeamtubePressure
M = species.mass
alpha = species.polarizability
rho = P / (kT) # number density of Gas
v0 = sqrt(2*kT / M) # mean speed of Gas
waist = cavity.w0 # Gaussian beam waist size
zr = cavity.zr # Rayleigh range
z1 = -cavity.zBeam_ITM # location of ITM relative to the waist
z2 = cavity.zBeam_ETM # location of ETM relative to the waist
# The exponential of Eq. 1 of P940008 is expanded to first order; this
# can be integrated analytically
zint = log(z2 + sqrt(z2**2 + zr**2)) - log(z1 + sqrt(z1**2 + zr**2))
zint = zint * zr/waist
zint = zint - 2*pi*L*f/v0
# optical path length for one arm
zint = zint*((4*rho*(2*pi*alpha)**2)/v0)
# eliminate any negative values due to first order approx.
zint[zint < 0] = 0
return zint
v0 = np.sqrt(2*kT / M) # most probable speed of Gas
alpha = species.polarizability
# put the integrand into a (numfreq, numpressure) array for faster
# integration with trapezoid
integrand = np.exp(np.einsum('i,j->ij', -2*np.pi*f, ww_m/v0))
integrand *= np.einsum('i,j->ij', np.ones_like(f), pressure_Pa / ww_m)
zint = trapezoid(integrand, tube_pos, axis=1)
noise = 4 * (2*np.pi*alpha)**2 / (v0 * kT) * zint
return noise
def residual_gas_damping_test_mass(f, ifo, species, sustf, squeezed_film):
......
......@@ -11,45 +11,40 @@ from .. import nb
def SuspensionThermal_constructor(stage_num, direction):
def SuspensionThermal_constructor(stage_name, 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 = '--'
assert direction in ["horiz", "vert"]
stage_map = {
"Top": 0,
"UIM": 1,
"PUM": 2,
"TM": 3,
}
color_map = {
"Top": "xkcd:orangeish",
"UIM": "xkcd:mustard",
"PUM": "xkcd:turquoise",
"TM": "xkcd:bright purple",
}
label_map = {
"TM": "Test mass",
}
class SuspensionThermalStage(nb.Noise):
name = name0
name = f"{direction.capitalize()}{stage_name}"
style = dict(
label=label,
color=color,
linestyle=linestyle,
label=f"{direction.capitalize()}. {label_map.get(stage_name, stage_name)}",
color=color_map[stage_name],
linestyle="-" if direction == "horiz" else "--",
alpha=0.7,
)
@nb.precomp(sustf=precomp_suspension)
def calc(self, sustf):
n = susptherm_stage(
self.freq, self.ifo.Suspension, sustf, stage_num, direction)
self.freq, self.ifo.Suspension, sustf, stage_map[stage_name], direction)
return abs(n) * 4
return SuspensionThermalStage
......@@ -68,13 +63,13 @@ class SuspensionThermal(nb.Budget):
)
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'),
SuspensionThermal_constructor("Top", "horiz"),
SuspensionThermal_constructor("UIM", "horiz"),
SuspensionThermal_constructor("PUM", "horiz"),
SuspensionThermal_constructor("TM", "horiz"),
SuspensionThermal_constructor("Top", "vert"),
SuspensionThermal_constructor("UIM", "vert"),
SuspensionThermal_constructor("PUM", "vert"),
]
......