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 (96)
Showing
with 911 additions and 1073 deletions
#for docs and setup.py outputs
build/
tresults/
tresults*/
test_results*/
# test cache
gwinc/test/cache
test/*/*.h5
gwinc.egg-info/
.eggs/
gwinc/_version.py
# Byte-compiled / optimized / DLL files
__pycache__/
......
......@@ -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()
```
......
......@@ -40,6 +40,14 @@ def pytest_addoption(parser):
help="Do not preclear tpaths",
)
parser.addoption(
"--generate",
action="store_true",
default=False,
dest="generate",
help="Generate test data",
)
@pytest.fixture
def plot(request):
......@@ -63,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)
......@@ -76,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'))
"""
......@@ -228,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")
......@@ -270,3 +278,83 @@ def relfile_test(_file_, request, pre = None, post = None, fname = None):
else:
return relfile(_file_, testname, fname = fname)
@pytest.fixture
def compare_noise(pprint):
"""
Fixture to compare two sets of traces
A list of noises passed, failed, and skipped are printed. Comparisons are
skipped if the psd's are sufficiently small (controlled by psd_tol) indicating
that the noise is essentially zero or if a trace is missing.
An assertion error is raised if any noises fail.
"""
import numpy as np
def compare(traces, ref_traces, psd_tol=1e-52):
passed = []
failed = []
skipped = []
if ref_traces.budget:
for ref_trace in ref_traces:
if np.all(ref_trace.psd < psd_tol):
skipped.append(ref_trace.name)
continue
try:
trace = traces[ref_trace.name]
except KeyError:
skipped.append(ref_trace.name)
continue
if np.allclose(trace.psd, ref_trace.psd, atol=0):
passed.append(trace.name)
else:
failed.append(trace.name)
else:
if np.allclose(ref_traces.psd, traces.psd, atol=0):
passed.append(traces.name)
else:
failed.append(traces.name)
pprint('Noises failed:')
pprint(40 * '-')
for noise in failed:
pprint(noise)
pprint(40 * '+')
pprint('Noises passed:')
pprint(40 * '-')
for noise in passed:
pprint(noise)
pprint(40 * '+')
pprint('Noises skipped:')
pprint(40 * '-')
for noise in skipped:
pprint(noise)
assert len(failed) == 0
return compare
def pytest_collection_modifyitems(config, items):
"""
Modifies tests to be selectively skipped with command line options
https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option
"""
# run tests marked as generate if and only if --generate is given
# skip all others in this case
if config.getoption('--generate'):
skip = pytest.mark.skip(
reason='only running tests that generate data')
for item in items:
if 'generate' not in item.keywords:
item.add_marker(skip)
else:
skip = pytest.mark.skip(
reason='generates test data: needs --generate to run')
for item in items:
if 'generate' in item.keywords:
item.add_marker(skip)
......@@ -44,8 +44,8 @@ def freq_from_spec(spec=None):
return spec
elif spec is None:
spec = DEFAULT_FREQ
fspec = spec.split(':')
try:
fspec = spec.split(':')
if len(fspec) == 2:
fspec = fspec[0], DEFAULT_FREQ.split(':')[1], fspec[1]
return np.logspace(
......@@ -53,7 +53,7 @@ def freq_from_spec(spec=None):
np.log10(float(fspec[2])),
int(fspec[1]),
)
except (ValueError, IndexError):
except (ValueError, IndexError, AttributeError):
raise InvalidFrequencySpec(f'Improper frequency specification: {spec}')
......@@ -125,22 +125,34 @@ def load_budget(name_or_path, freq=None, bname=None):
if inherit_ifo is not None:
del ifo['+inherit']
# make the inherited path relative to the loaded path
# if it is a yml file
if os.path.splitext(inherit_ifo)[1] in Struct.STRUCT_EXT:
base = os.path.split(path)[0]
inherit_ifo = os.path.join(base, inherit_ifo)
# if it is a yml file or a directory
head = os.path.split(path)[0]
rel_path = os.path.join(head, inherit_ifo)
if os.path.splitext(inherit_ifo)[1] in Struct.STRUCT_EXT or os.path.exists(rel_path):
inherit_ifo = rel_path
inherit_budget = load_budget(inherit_ifo, freq=freq, bname=bname)
pre_ifo = inherit_budget.ifo
pre_ifo.update(ifo, overwrite_atoms=False)
pre_ifo.update(
ifo,
overwrite_atoms=False,
clear_test=lambda v: isinstance(v, str) and v == '<unset>'
)
inherit_budget.update(ifo=pre_ifo)
return inherit_budget
else:
modname = 'gwinc.ifo.aLIGO'
else:
bname = bname or 'aLIGO'
elif ext == '':
bname = bname or base
modname = path
else:
raise RuntimeError(
"Unknown file type: {} (supported types: {}).".format(
ext, Struct.STRUCT_EXT))
else:
if name_or_path not in IFOS:
raise RuntimeError("Unknown IFO '{}' (available IFOs: {}).".format(
......@@ -154,7 +166,7 @@ def load_budget(name_or_path, freq=None, bname=None):
mod, modpath = load_module(modname)
Budget = getattr(mod, bname)
if freq is None:
freq = getattr(Budget, 'freq', None)
freq = getattr(Budget, '_freq', None)
freq = freq_from_spec(freq)
ifopath = os.path.join(modpath, 'ifo.yaml')
if not ifo and os.path.exists(ifopath):
......
......@@ -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.noises import *
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
import gwinc.ifo.noises as calibrations
class QuantumVacuum(nb.Budget):
"""Quantum Vacuum
class Aplus(nb.Budget):
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
name = 'A+'
noises = [
QuantumVacuumAS,
QuantumVacuumArm,
QuantumVacuumSEC,
QuantumVacuumFilterCavity,
QuantumVacuumInjection,
QuantumVacuumReadout,
QuantumVacuumQuadraturePhase,
noise.quantum.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 = [
calibrations.Strain,
]
class Aplus(nb.Budget):
plot_style = PLOT_STYLE
name = 'A+'
noises = [
QuantumVacuum,
Seismic,
Newtonian,
SuspensionThermal,
CoatingBrownian,
CoatingThermoOptic,
SubstrateBrownian,
SubstrateThermoElastic,
ExcessGas,
class Displacement(Aplus):
calibrations = []
class Acceleration(Aplus):
calibrations = [
calibrations.Acceleration,
]
class Velocity(Aplus):
calibrations = [
Strain,
calibrations.Velocity,
]
plot_style = PLOT_STYLE
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.noises import *
from gwinc.ifo import PLOT_STYLE
class QuantumVacuum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
QuantumVacuumAS,
QuantumVacuumArm,
QuantumVacuumSEC,
QuantumVacuumFilterCavity,
QuantumVacuumInjection,
QuantumVacuumReadout,
QuantumVacuumQuadraturePhase,
]
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
NewtonianRayleigh,
NewtonianBody,
NewtonianInfrasound,
]
from gwinc import noise
from gwinc import nb
import gwinc.ifo.noises as calibrations
class Coating(nb.Budget):
......@@ -54,8 +17,8 @@ class Coating(nb.Budget):
)
noises = [
CoatingBrownian,
CoatingThermoOptic,
noise.coatingthermal.CoatingBrownian,
noise.coatingthermal.CoatingThermoOptic,
]
......@@ -72,8 +35,8 @@ class Substrate(nb.Budget):
)
noises = [
SubstrateBrownian,
SubstrateThermoElastic,
noise.substratethermal.SubstrateBrownian,
noise.substratethermal.SubstrateThermoElastic,
]
......@@ -82,17 +45,39 @@ class CE1(nb.Budget):
name = 'Cosmic Explorer 1'
noises = [
QuantumVacuum,
Seismic,
Newtonian,
SuspensionThermal,
noise.quantum.Quantum,
noise.seismic.Seismic,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
Coating,
Substrate,
ExcessGas,
noise.residualgas.ResidualGas,
]
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.noises import *
from gwinc.ifo import PLOT_STYLE
class QuantumVacuum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
QuantumVacuumAS,
QuantumVacuumArm,
QuantumVacuumSEC,
QuantumVacuumFilterCavity,
QuantumVacuumInjection,
QuantumVacuumReadout,
QuantumVacuumQuadraturePhase,
]
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
NewtonianRayleigh,
NewtonianBody,
NewtonianInfrasound,
]
from gwinc import noise
from gwinc import nb
import gwinc.ifo.noises as calibrations
class Coating(nb.Budget):
......@@ -54,8 +17,8 @@ class Coating(nb.Budget):
)
noises = [
CoatingBrownian,
CoatingThermoOptic,
noise.coatingthermal.CoatingBrownian,
noise.coatingthermal.CoatingThermoOptic,
]
......@@ -72,8 +35,8 @@ class Substrate(nb.Budget):
)
noises = [
SubstrateBrownian,
SubstrateThermoElastic,
noise.substratethermal.SubstrateBrownian,
noise.substratethermal.SubstrateThermoElastic,
]
......@@ -82,17 +45,39 @@ class CE2silica(nb.Budget):
name = 'Cosmic Explorer 2 (Silica)'
noises = [
QuantumVacuum,
Seismic,
Newtonian,
SuspensionThermal,
noise.quantum.Quantum,
noise.seismic.Seismic,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
Coating,
Substrate,
ExcessGas,
noise.residualgas.ResidualGas,
]
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.noises import *
from gwinc.ifo import PLOT_STYLE
class QuantumVacuum(nb.Budget):
"""Quantum Vacuum
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
noises = [
QuantumVacuumAS,
QuantumVacuumArm,
QuantumVacuumSEC,
QuantumVacuumFilterCavity,
QuantumVacuumInjection,
QuantumVacuumReadout,
QuantumVacuumQuadraturePhase,
]
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
NewtonianRayleigh,
NewtonianBody,
NewtonianInfrasound,
]
from gwinc import noise
from gwinc import nb
import gwinc.ifo.noises as calibrations
class Coating(nb.Budget):
......@@ -54,8 +17,8 @@ class Coating(nb.Budget):
)
noises = [
CoatingBrownian,
CoatingThermoOptic,
noise.coatingthermal.CoatingBrownian,
noise.coatingthermal.CoatingThermoOptic,
]
......@@ -72,9 +35,9 @@ class Substrate(nb.Budget):
)
noises = [
ITMThermoRefractive,
SubstrateBrownian,
SubstrateThermoElastic,
noise.substratethermal.ITMThermoRefractive,
noise.substratethermal.SubstrateBrownian,
noise.substratethermal.SubstrateThermoElastic,
]
......@@ -83,17 +46,43 @@ class CE2silicon(nb.Budget):
name = 'Cosmic Explorer 2 (Silicon)'
noises = [
QuantumVacuum,
Seismic,
Newtonian,
SuspensionThermal,
noise.quantum.Quantum,
noise.seismic.Seismic,
noise.newtonian.Newtonian,
noise.suspensionthermal.SuspensionThermal,
Coating,
Substrate,
ExcessGas,
noise.residualgas.ResidualGas,
]
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.noises import *
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
import gwinc.ifo.noises as calibrations
class QuantumVacuum(nb.Budget):
"""Quantum Vacuum
class Voyager(nb.Budget):
"""
style = dict(
label='Quantum Vacuum',
color='#ad03de',
)
name = 'Voyager'
noises = [
QuantumVacuumAS,
QuantumVacuumArm,
QuantumVacuumSEC,
QuantumVacuumFilterCavity,
QuantumVacuumInjection,
QuantumVacuumReadout,
noise.quantum.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 = [
calibrations.Strain,
]
class Voyager(nb.Budget):
plot_style = PLOT_STYLE
name = 'Voyager'
noises = [
QuantumVacuum,
Seismic,
Newtonian,
SuspensionThermal,
CoatingBrownian,
CoatingThermoOptic,
ITMThermoRefractive,
SubstrateBrownian,
SubstrateThermoElastic,
ExcessGas,
class Displacement(Voyager):
calibrations = []
class Acceleration(Voyager):
calibrations = [
calibrations.Acceleration,
]
class Velocity(Voyager):
calibrations = [
Strain,
calibrations.Velocity,
]
plot_style = PLOT_STYLE
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,12 +387,13 @@ 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:
fdetune: -25 # detuning [Hz] zz['x'][0][1]
fdetune: -27.3 # detuning [Hz] zz['x'][0][1]
L: 300 # cavity length [m]
Ti: 6.15e-4 # input mirror transmission [Power] zz['x'][0][2]
Ti: 6.88e-4 # input mirror transmission [Power] zz['x'][0][2]
Te: 0e-6 # end mirror transmission
Lrt: 10e-6 # round-trip loss in the cavity
Rot: 0 # phase rotation after cavity
......
from gwinc.ifo.noises import *
from gwinc.ifo import PLOT_STYLE
from gwinc import noise
from gwinc import nb
import gwinc.ifo.noises as calibrations
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,19 +26,41 @@ 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 = [
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'
......
This diff is collapsed.
......@@ -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.
......@@ -139,9 +157,8 @@ class BudgetItem:
variables to the initialized object.
"""
if freq is not None:
assert isinstance(freq, np.ndarray)
self.freq = freq
assert isinstance(freq, np.ndarray) or freq is None
self._freq = freq
self.ifo = None
for key, val in kwargs.items():
setattr(self, key, val)
......@@ -153,6 +170,20 @@ class BudgetItem:
""""Name of this BudgetItem class."""
return self.__class__.__name__
@property
def freq(self):
"""Frequency array [Hz]"""
return self._freq
@freq.setter
def freq(self, val):
assert isinstance(val, np.ndarray)
# clear the precomp cache
self._precomp = dict()
# use update instead of setting _freq directly so that Budget.update
# recurses through all cal_objs and noise_objs
self.update(_freq=val)
def __str__(self):
# FIXME: provide info on internal state (load/update/calc/etc.)
return '<{} {}>'.format(
......@@ -271,7 +302,7 @@ class Noise(BudgetItem):
ifo_hash = ifo.hash(ifo._orig_keys)
if ifo_hash != getattr(self, '_ifo_hash', 0):
logger.debug("ifo hash change")
kwargs['ifo'] = self.ifo
kwargs['ifo'] = ifo
self._ifo_hash = ifo_hash
if kwargs:
......@@ -326,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)"""
......@@ -353,7 +389,7 @@ class Budget(Noise):
if freq is not None:
self.kwargs['freq'] = freq
else:
self.kwargs['freq'] = getattr(self, 'freq', None)
self.kwargs['freq'] = getattr(self, '_freq', None)
# FIXME: special casing the ifo kwarg here, in case it's
# defined as a class attribute rather than passed at
# initialization. we do this because we're not defining a
......@@ -369,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:
......@@ -498,7 +539,7 @@ class Budget(Noise):
logger.debug("load {}".format(item))
item.load()
def update(self, _precomp=None, **kwargs):
def update(self, **kwargs):
"""Recursively update all noise and cal objects with supplied kwargs.
See BudgetItem.update() for more info.
......@@ -572,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
......@@ -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
......@@ -9,6 +9,69 @@ import scipy.special as sp
from .seismic import seismic_ground_NLNM
from .. import const
from .. import nb
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
class Newtonian(nb.Budget):
"""Newtonian Gravity
"""
name = 'Newtonian'
style = dict(
label='Newtonian',
color='#15b01a',
)
noises = [
Rayleigh,
Body,
Infrasound,
]
def gravg(f, seismic):
......
......@@ -10,13 +10,190 @@ from collections.abc import Sequence
from .. import logger
from .. import const
from ..struct import Struct
from .. import nb
from ..suspension import precomp_suspension
def sqzOptimalSqueezeAngle(Mifo, eta):
vHD = np.array([[sin(eta), cos(eta)]])
H = getProdTF(vHD, Mifo)[0]
alpha = arctan(abs(H[1]), abs(H[0]))
return alpha
@nb.precomp(sustf=precomp_suspension)
def precomp_quantum(f, ifo, sustf):
from ..ifo import noises
pc = Struct()
power = noises.ifo_power(ifo)
noise_dict = shotrad(f, ifo, sustf, power)
pc.ASvac = noise_dict['ASvac']
pc.SEC = noise_dict['SEC']
pc.Arm = noise_dict['arm']
pc.Injection = noise_dict['injection']
pc.PD = noise_dict['pd']
# FC0 are the noises from the filter cavity losses and FC0_unsqzd_back
# are noises from the unsqueezed vacuum injected at the back mirror
# Right now there are at most one filter cavity in all the models;
# if there were two, there would also be FC1 and FC1_unsqzd_back, etc.
# keys = list(noise_dict.keys())
fc_keys = [key for key in noise_dict.keys() if 'FC' in key]
pc.FC = np.zeros_like(pc.ASvac)
if fc_keys:
for key in fc_keys:
pc.FC += noise_dict[key]
if 'phase' in noise_dict.keys():
pc.Phase = noise_dict['phase']
if 'ofc' in noise_dict.keys():
pc.OFC = noise_dict['OFC']
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 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
"""
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 getSqzParams(ifo):
......@@ -33,14 +210,16 @@ def getSqzParams(ifo):
params = Struct()
if 'Squeezer' not in ifo:
sqzType = None
sqzType = 'None'
elif ifo.Squeezer.AmplitudedB == 0:
sqzType = 'None'
else:
sqzType = ifo.Squeezer.get('Type', 'Freq Independent')
params.sqzType = sqzType
# extract squeezer parameters
if sqzType is None:
if sqzType == 'None':
params.SQZ_DB = 0
params.ANTISQZ_DB = 0
params.alpha = 0
......