Skip to content
Snippets Groups Projects
Commit 97afd5d6 authored by Jameson Rollins's avatar Jameson Rollins
Browse files

precomp support

Functions listed in the BudgetItem.precomp list are all executed by the
update() method (after attribute update).  Execution state is cached at
the Budget level, to prevent re-calculating the same functions multiple
per update.

This provides a speed-up of about 50% for the Aplus model, but much less
for the CE models which are limited by other noise calculations.
parent 83625807
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !94. Comments created here will be created in the context of that merge request.
......@@ -351,6 +351,47 @@ The IFOs included in `gwinc.ifo` provide examples of the use of the
budget interface (e.g. [gwinc.ifo.aLIGO](gwinc/ifo/aLIGO)).
### "precomp" functions
`BudgetItems` support "precomp" functions that are calculated during
the `update()` method call and can be used to cache common derived
values in the `ifo` struct for later use. The execution state of
these functions is cached at the Budget level, to prevent needlessly
re-calculating them in multiple BudgetItems. When they are calculated
they are provided with the `freq` and `ifo` attributes as arguments.
For example, take the following budget definition:
```python
class Noise0(Noise):
precomp = [
precomp_foo,
]
...
class Noise1(Noise):
precomp = [
precomp_foo,
precomp_bar,
]
...
class MyBudget(Budget):
noises = [
Noise0,
Noise1,
]
```
When the `MyBudget.update()` method is called, all the `precomp`
functions will be execute:
```python
precomp_foo(self.freq, self.ifo)
precomp_bar(self.freq, self.ifo)
```
But the `precomp_foo` function will only be calculated once per budget
update() call, even though it's specified as needed by both `Noise0`
and `Noise1`.
### extracting single noise terms
There are various way to extract single noise terms from the Budget
......
......@@ -35,6 +35,7 @@ def main():
range_pad = max(len(name), range_pad)
for name, budget in budgets.items():
budget.update()
data = budget.calc()
try:
import inspiral_range
......
......@@ -127,15 +127,6 @@ def precomp_mirror(f, ifo):
* ifo.Materials.Substrate.MassDensity
def precomp_gwinc(f, ifo):
ifo.gwinc = Struct()
pbs, parm, finesse, prfactor, Tpr = ifo_power(ifo)
ifo.gwinc.pbs = pbs
ifo.gwinc.parm = parm
ifo.gwinc.finesse = finesse
ifo.gwinc.prfactor = prfactor
def precomp_suspension(f, ifo):
if 'VHCoupling' not in ifo.Suspension:
ifo.Suspension.VHCoupling = Struct()
......@@ -146,6 +137,31 @@ def precomp_suspension(f, ifo):
ifo.Suspension.hTable = hTable
ifo.Suspension.vTable = vTable
def precomp_coating(f, ifo):
ifo.Optics.ITM.dOpt = coating_thickness(ifo, 'ITM')
ifo.Optics.ETM.dOpt = coating_thickness(ifo, 'ETM')
def precomp_power(f, ifo):
if 'gwinc' not in ifo:
ifo.gwinc = Struct()
pbs, parm, finesse, prfactor, Tpr = ifo_power(ifo)
ifo.gwinc.pbs = pbs
ifo.gwinc.parm = parm
ifo.gwinc.finesse = finesse
ifo.gwinc.gPhase = finesse * 2/np.pi
ifo.gwinc.prfactor = prfactor
def precomp_cavity(f, ifo):
if 'gwinc' not in ifo:
ifo.gwinc = Struct()
w0, wBeam_ITM, wBeam_ETM = arm_cavity(ifo)
ifo.gwinc.w0 = w0
ifo.gwinc.wBeam_ITM = wBeam_ITM
ifo.gwinc.wBeam_ETM = wBeam_ETM
##################################################
class Strain(nb.Calibration):
......@@ -164,9 +180,12 @@ class QuantumVacuum(nb.Noise):
color='#ad03de',
)
precomp = [
precomp_mirror,
precomp_power,
]
def calc(self):
precomp_mirror(self.freq, self.ifo)
precomp_gwinc(self.freq, self.ifo)
return noise.quantum.shotrad(self.freq, self.ifo)
......@@ -193,8 +212,11 @@ class Seismic(nb.Noise):
color='#855700',
)
precomp = [
precomp_suspension,
]
def calc(self):
precomp_suspension(self.freq, self.ifo)
if 'PlatformMotion' in self.ifo.Seismic:
if self.ifo.Seismic.PlatformMotion == 'BSC':
nt, nr = noise.seismic.seismic_BSC_ISI(self.freq)
......@@ -275,8 +297,11 @@ class SuspensionThermal(nb.Noise):
color='#0d75f8',
)
precomp = [
precomp_suspension,
]
def calc(self):
precomp_suspension(self.freq, self.ifo)
n = noise.suspensionthermal.suspension_thermal(self.freq, self.ifo.Suspension)
return n * 4
......@@ -290,16 +315,20 @@ class CoatingBrownian(nb.Noise):
color='#fe0002',
)
precomp = [
precomp_cavity,
precomp_coating,
]
def calc(self):
wavelength = self.ifo.Laser.Wavelength
materials = self.ifo.Materials
w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo)
dOpt_ITM = coating_thickness(self.ifo, 'ITM')
dOpt_ETM = coating_thickness(self.ifo, 'ETM')
nITM = noise.coatingthermal.coating_brownian(
self.freq, materials, wavelength, wBeam_ITM, dOpt_ITM)
self.freq, materials, wavelength,
self.ifo.gwinc.wBeam_ITM, self.ifo.Optics.ITM.dOpt)
nETM = noise.coatingthermal.coating_brownian(
self.freq, materials, wavelength, wBeam_ETM, dOpt_ETM)
self.freq, materials, wavelength,
self.ifo.gwinc.wBeam_ETM, self.ifo.Optics.ETM.dOpt)
return (nITM + nETM) * 2
......@@ -313,16 +342,20 @@ class CoatingThermoOptic(nb.Noise):
linestyle='--',
)
precomp = [
precomp_cavity,
precomp_coating,
]
def calc(self):
wavelength = self.ifo.Laser.Wavelength
materials = self.ifo.Materials
w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo)
dOpt_ITM = coating_thickness(self.ifo, 'ITM')
dOpt_ETM = coating_thickness(self.ifo, 'ETM')
nITM, junk1, junk2, junk3 = noise.coatingthermal.coating_thermooptic(
self.freq, materials, wavelength, wBeam_ITM, dOpt_ITM[:])
self.freq, materials, wavelength,
self.ifo.gwinc.wBeam_ITM, self.ifo.Optics.ITM.dOpt[:])
nETM, junk1, junk2, junk3 = noise.coatingthermal.coating_thermooptic(
self.freq, materials, wavelength, wBeam_ETM, dOpt_ETM[:])
self.freq, materials, wavelength,
self.ifo.gwinc.wBeam_ETM, self.ifo.Optics.ETM.dOpt[:])
return (nITM + nETM) * 2
......@@ -336,13 +369,15 @@ class ITMThermoRefractive(nb.Noise):
linestyle='--',
)
precomp = [
precomp_power,
precomp_cavity,
]
def calc(self):
finesse = ifo_power(self.ifo)[2]
gPhase = finesse * 2/np.pi
w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo)
n = noise.substratethermal.substrate_thermorefractive(
self.freq, self.ifo.Materials, wBeam_ITM)
return n * 2 / gPhase**2
self.freq, self.ifo.Materials, self.ifo.gwinc.wBeam_ITM)
return n * 2 / self.ifo.gwinc.gPhase**2
class ITMCarrierDensity(nb.Noise):
......@@ -355,13 +390,15 @@ class ITMCarrierDensity(nb.Noise):
linestyle='--',
)
precomp = [
precomp_power,
precomp_cavity,
]
def calc(self):
finesse = ifo_power(self.ifo)[2]
gPhase = finesse * 2/np.pi
w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo)
n = noise.substratethermal.substrate_carrierdensity(
self.freq, self.ifo.Materials, wBeam_ITM)
return n * 2 / gPhase**2
self.freq, self.ifo.Materials, self.ifo.gwinc.wBeam_ITM)
return n * 2 / self.ifo.gwinc.gPhase**2
class SubstrateBrownian(nb.Noise):
......@@ -374,12 +411,15 @@ class SubstrateBrownian(nb.Noise):
linestyle='--',
)
precomp = [
precomp_cavity,
]
def calc(self):
w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo)
nITM = noise.substratethermal.substrate_brownian(
self.freq, self.ifo.Materials, wBeam_ITM)
self.freq, self.ifo.Materials, self.ifo.gwinc.wBeam_ITM)
nETM = noise.substratethermal.substrate_brownian(
self.freq, self.ifo.Materials, wBeam_ETM)
self.freq, self.ifo.Materials, self.ifo.gwinc.wBeam_ETM)
return (nITM + nETM) * 2
......@@ -393,12 +433,15 @@ class SubstrateThermoElastic(nb.Noise):
linestyle='--',
)
precomp = [
precomp_cavity,
]
def calc(self):
w0, wBeam_ITM, wBeam_ETM = arm_cavity(self.ifo)
nITM = noise.substratethermal.substrate_thermoelastic(
self.freq, self.ifo.Materials, wBeam_ITM)
self.freq, self.ifo.Materials, self.ifo.gwinc.wBeam_ITM)
nETM = noise.substratethermal.substrate_thermoelastic(
self.freq, self.ifo.Materials, wBeam_ETM)
self.freq, self.ifo.Materials, self.ifo.gwinc.wBeam_ETM)
return (nITM + nETM) * 2
......
......@@ -31,15 +31,44 @@ class BudgetItem:
"""
return None
def update(self, **kwargs):
"""Overload method for updating data.
def update(self, _precomp=None, **kwargs):
"""Update parameters and execute precomp functions.
By default any keyword arguments provided are written directly
as attribute variables (as with __init__).
The method does two things. First, any keyword arguments
provided are written directly as attribute variables to the
class (as with __init__).
Second, after attribute update, all functions listed in the
`precomp` attribute are executed, supplied with the `freq` and
`ifo` attributes. So if the `precomp` attribute is defined
as:
precomp = [precomp_foo, precomp_bar]
then this method will execute the following after attribute
update:
precomp_foo(self.freq, self.ifo)
precomp_bar(self.freq, self.ifo)
These functions are intended to update the `ifo` Struct
attribute with derived values cached for later usage. If the
`_precomp` argument is provided to this method then it is
assumed to be a set of previously executed precomp functions,
and any function already listed there will not be re-executed,
and it will be updated with any newly executed functions.
"""
for key, val in kwargs.items():
setattr(self, key, val)
for func in getattr(self, 'precomp', []):
if _precomp is None:
_precomp = set()
if func in _precomp:
continue
logger.debug("precomp {}".format(func))
func(self.freq, self.ifo)
_precomp.add(func)
def calc(self):
"""Overload method for final PSD calculation.
......@@ -361,13 +390,15 @@ class Budget(Noise):
logger.debug("load {}".format(item))
item.load()
def update(self, **kwargs):
def update(self, _precomp=None, **kwargs):
"""Update all noise and cal objects with supplied kwargs."""
if _precomp is None:
_precomp = set()
for name, item in itertools.chain(
self._cal_objs.items(),
self._noise_objs.items()):
logger.debug("update {}".format(item))
item.update(**kwargs)
item.update(_precomp=_precomp, **kwargs)
def calc_noise(self, name):
"""Return calibrated individual noise term.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment