diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d9e74ff44eff5b48ea5eac3f0d0f6648fd6203f..e7b2de49312cde25b824aef9f5809dc294dffdca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,8 +5,22 @@ all contributions should be peer-reviewed. To facilitate the review, please do not commit your work to this repository yourself. Instead, fork the repository and send a merge request. +`pygwinc` comes with a test suite that will calculate all canonical +IFO budgets with the current state of the code, and compare them +against cached hdf5 traces (stored in the repo with +[git-lfs](https://git-lfs.github.com/) ( [see +also](https://wiki.ligo.org/Computing/GitLFS)). Contributors should +run the tests before submitting any merge requests: +```shell +$ python3 -m gwinc.test +``` +The test suite will be run automatically upon push to git.ligo.org as +part of the continuous integration, and code that fails the CI will +not be merged, unless failures are well justified. + If your change affects the shape of a noise curve, your commit message should make note of that, and provide a justification. It will also -be necessary to update the reference curves stored in DCC entry -[T18xxxxx](https://dcc.ligo.org/LIGO-T18xxxxx), after the change is -applied. +be necessary to update the reference curves stored in cache, but that +should be done in a separate commit not a part of the original merge +request, so that all reviewers can see the changes introduced in the +CI test failure report. diff --git a/README.md b/README.md index af9fdbee01bf17541ae87fe9be4690cb75a02570..5718f15b42d11f849e7f79557b6773c1f7c7213a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,89 @@ [](https://git.ligo.org/gwinc/pygwinc/commits/master) -# Python port of GW Interferometer Noise Calculator +# Python Gravitational Wave Interferometer Noise Calculator  -This is a collection of mostly analytic noise calculations (e.g. quantum, thermal) +`pygwinc` is a multi-faceted tool for processing and plotting noise +budgets for ground-based gravitational wave detectors. It's primary +feature is a collection of mostly analytic noise calculation functions +for various sources of noise affecting detectors (see [noise +functions](#noise-functions) below): +* quantum noise +* mirror coating thermal noise +* mirror substrate thermal noise +* suspension fiber thermal noise +* seismic noise +* Newtonian/gravity-gradient noise +* residual gas noise -## basic usage +`pygwinc` is also a generalized noise budgeting tool, allowing users +to create arbitrary noise budgets (for any experiment, not just +ground-based GW detectors) using measured or analytically calculated +data. See the [budget interface](#budget-interface) section below. -`pygwinc` creates noise budgets based on detector descriptions -provided in either .yml or .mat files (see below). Once the detector -description is loaded, the noise budget can be calculated and plotted: +`pygwinc` includes canonical budgets for various well-known current +and future detectors: + +* [aLIGO](https://gwinc.docs.ligo.org/pygwinc/aLIGO.png) +* [A+](https://gwinc.docs.ligo.org/pygwinc/Aplus.png) +* [Voyager](https://gwinc.docs.ligo.org/pygwinc/Voyager.png) +* [Cosmic Explorer 1](https://gwinc.docs.ligo.org/pygwinc/CE1.png) +* [Cosmic Explorer 2](https://gwinc.docs.ligo.org/pygwinc/CE2.png) + +See [IFO.md](IFO.md) for the latest CI-generated plots and hdf5 cached +data. + +For calculating various common "inspiral range" figures of merit +please see the +[`inspiral_range`](https://git.ligo.org/gwinc/inspiral-range) package. + + +## usage + +### command line interface + +`pygwinc` provides a command line interface that can be used to +calculate and plot noise budgets for the various canonical IFOs +described above, save/plot hdf5 trace data, and dump budget IFO +parameters: +```shell +$ python3 -m gwinc aLIGO +``` + +You can play with IFO parameters and see the effects on the budget by +dumping the pre-defined parameters to a [YAML-formatted parameter +file](#yaml-parameter-files), editing the parameter file, and +re-calculating the noise budget: +```shell +$ python3 -m gwinc --yaml aLIGO > my_aLIGO.yaml +$ edit my_aLIGO.yaml +$ python3 -m gwinc -d my_aLIGO.yaml aLIGO + aLIGO my_aLIGO.yaml +Materials.Coating.Philown 5e-05 3e-05 +$ python3 -m gwinc my_aLIGO.yaml +``` + +Stand-alone YAML files will always assume the nominal ['aLIGO' budget +description](gwinc/ifo/aLIGO). + +[Custom budgets](#budget-interface) may also be processed by providing +the path to the budget module/package: +```shell +$ python3 -m gwinc path/to/mybudget +``` + +See command help for more info: +```shell +$ python3 -m gwinc -h +``` + + +### basic python library usage + +For custom code, parameter optimization, etc. all functionality can be +accessed through the `gwinc` library interface: ```python >>> import gwinc >>> import numpy as np @@ -22,54 +94,120 @@ description is loaded, the noise budget can be calculated and plotted: >>> fig.show() ``` +The `load_budget()` function takes most of the same inputs as the +command line interface (e.g. IFO names, budget module paths, YAML +parameter files), and returns the un-instantiated Budget class defined +in the specified budget module. -## command line interface -You can make gwinc plots directly from the command line by executing -the package directly: -```shell -$ python3 -m gwinc aLIGO +## noise functions + +`pygwinc` noise functions are available in the `gwinc.noise` package. +This package includes multiple modules for the different types of +noises, e.g. `suspensionthermal`, `coatingthermal`, `quantum`, etc.) + +The various noise functions need many different parameters to +calculate their noise outputs. The parameters are expected to be in +the form of object attributes, e.g.: +```python +def coating_brownian(f, materials, wavelength, wBeam, dOpt): + ... + # extract substructures + sub = materials.Substrate + ... + # substrate properties + Ysub = sub.MirrorY ``` +The `materials` input argument in this case is expected to be an +object with a `Substrate` attribute, which itself has a `MirrorY` +attribute. + + +### `gwinc.Struct` objects -## detector description files +To make all this easier `pygwinc` provides a `Struct` class that can +hold parameters in attributes and additionally acts like a dictionary. +`Struct`s can be created from dictionaries, or loaded from various +file formats (see below). -`pygwinc` can load budget descriptions in different formats: python -package/module, .yaml YAML file, and MATLAB gwinc .mat or .m files. -`pygwinc` includes budgets for various canonical detectors: +### YAML parameter files + +The easiest way to store all budget parameters is in a YAML file. +YAML files can be loaded directly into `gwinc.Struct` objects via +the `Struct.from_file()` class method: +```python +from gwinc import Struct +ifo = Struct.from_file('/path/to/ifo.yaml') +``` -* [aLIGO](https://git.ligo.org/gwinc/pygwinc/blob/master/gwinc/ifo/aLIGO) -* [A+](https://git.ligo.org/gwinc/pygwinc/blob/master/gwinc/ifo/Aplus) -* [Voyager](https://git.ligo.org/gwinc/pygwinc/blob/master/gwinc/ifo/Voyager) -* [Cosmic Explorer](https://git.ligo.org/gwinc/pygwinc/blob/master/gwinc/ifo/CE) +YAML parameter files can also be given to the `load_budget()` function +as described above, in which case the base 'aLIGO' budget structure +will be assumed and returned, with the YAML Struct inserted in the +`Budget.ifo` class attribute. +Here are the included ifo.yaml files for all the canonical IFOs: -## noise budgets +* [aLIGO.yaml](gwinc/ifo/aLIGO/ifo.yaml) +* [Aplus.yaml](gwinc/ifo/Aplus/ifo.yaml) +* [Voyager.yaml](gwinc/ifo/Voyager/ifo.yaml) +* [CE1.yaml](gwinc/ifo/CE1/ifo.yaml) +* [CE2.yaml](gwinc/ifo/CE2/ifo.yaml) +The `Struct.from_file()` class method can also load MATLAB structs +defined in .mat files, for compatibility with +[matgwinc](https://git.ligo.org/gwinc/matgwinc), and MATLAB .m files, +although the later requires the use of the [python MATLAB +engine](https://www.mathworks.com/help/matlab/matlab-engine-for-python.html). -GWINC provides an `nb` package for defining arbitrary noise budgets: + +## budget interface + +The basic structure of a `pygwinc` noise budget is a "budget module". +A budget modules is a standard python module (single `.py` file) or +package (directory containing `__inti__.py` file) which contains at +least one `nb.Budget` class definition named after the module name. + +The `gwinc.nb` package provides various `BudgetItem` classes that can +inherited to define the various components of a budget: + +* `nb.Noise`: BudgetItem describing a noise source +* `nb.Calibration`: BudgetItem describing a noise calibration +* `nb.Budget`: BudgetItem describing a group of noises + +Here's an example of a budget module name `MyBudget`: +```shell +$ find MyBudget +MyBudget/ +MyBudget/__init__.py +MyBudget/ifo.yaml +$ +``` ```python +# MyBudget/__init__.py + import numpy as np from gwinc import nb from gwinc import noise -class ExcessGas(nb.Noise): - """Excess gas""" +class SuspensionThermal(nb.Noise): + """Suspension thermal noise""" style = dict( - label='Excess Gas', + label='Suspension Thermal', color='#ad900d', linestyle='--', ) def calc(self): - return noise.residualgas.gas(self.freq, self.ifo) + n = noise.suspensionthermal.suspension_thermal( + self.freq, self.ifo.sus) + return 2 * n class MeasuredNoise(nb.Noise): - """My measured noise""" style = dict( label='Measured Noise', color='#838209', @@ -78,40 +216,108 @@ class MeasuredNoise(nb.Noise): def load(self): psd, freq = np.loadtxt('/path/to/measured/psd.txt') - self.data = self.interpolate(f, psd) + self.data = self.interpolate(freq, psd) def calc(self): return self.data +class MyCalibration(nb.Calibration): + def calc(self): + return np.ones_like(self.freq) * 1234 + + class MyBudget(nb.Budget): noises = [ - ExcessGas, + SuspensionThermal, MeasuredNoise, ] + + calibrations = [ + MyCalibration, + ] ``` - -## comparison with MATLAB gwinc - -`pygwinc` includes the ability use MATLAB gwinc directly via the -MATLAB python interface (see the CLI '--matlab' option above). This -also allows for easy direct comparison between the pygwinc and -matgwinc noise budgets. - -If you have a local checkout of matgwinc (at e.g. /path/to/gwinc) and -a local installation of MATLAB and it's python interface (at -e.g. /opt/matlab/python/lib/python3.6/site-packages) you can run the -comparison as so: -```shell -$ export GWINCPATH=/path/to/matgwinc -$ export PYTHONPATH=/opt/matlab/python/lib/python3.6/site-packages -$ python3 -m gwinc.test -p aLIGO +This budget would then be loaded with the `gwinc.load_budget()` +function, and processed with the `Budget.run()` method: +```python +Budget = load_budget('/path/to/MyBudget') +budget = Budget(freq) +traces = budget.run() ``` -This will produce a summary page of the various noise spectra that -differ between matgwinc and pygwinc. -Latest comparison plots from continuous integration: +Other than the necessary `frequency` Budget init argument, any +additional keyword arguments are assigned as class attributes to the +budget object, and to all of it's constituent sub +noises/calibrations/budgets. + +Note that the `SuspensionThermal` Noise class above uses the +`suspension_thermal` analytic noise calculation function, which takes +a "suspension" Struct as input argument. In this case, this +suspension Struct is extracted from the `self.ifo` Struct at +`self.ifo.sus`. + +If a budget module defined as a package includes an `ifo.yaml` +[parameter file](#parameter-files) in the package directory, the +`load_budget()` function will automatically load the YAML data into a +`gwinc.Struct` and include it as an `Budget.ifo` attribute in the +returned `Budget` class. This would provide the `self.ifo` needed in +the `SuspensionThermal` Noise class above and is therefore a +convenient way to provide parameter structures in budget packages. +Otherwise it would need to be created/loaded in some other way and +passed to the budget at instantiation, e.g.: +```python +Budget = load_budget('/path/to/MyBudget') +ifo = Struct.from_file('/path/to/MyBudget.ifo') +budget = Budget(freq, ifo=ifo) +traces = budget.run() +``` -* [aLIGO comparison](https://gwinc.docs.ligo.org/pygwinc/aLIGO_test.png) -* [A+ comparison](https://gwinc.docs.ligo.org/pygwinc/A+_test.png) +The IFOs included in `gwinc.ifo` provide examples of the use of the +budget interface: + +* [aLIGO](gwinc/ifo/aLIGO) +* [Aplus](gwinc/ifo/Aplus) +* [Voyager](gwinc/ifo/Voyager) +* [CE1](master/gwinc/ifo/CE1) +* [CE2](master/gwinc/ifo/CE2) + + +### BudgetItem load/update/calc methods + +The Noise/Calibration/Budget `BudgetItem`s have three core methods +that can be overridden by the user to handle arbitrary data +processing. This is useful for creating budgets from "live" dynamic +noise measurements and the like: + +* `load()`: initial loading of static data +* `update(**kwargs)`: update data/attributes +* `calc()`: return final data array + +See the built-in documentation for more info (e.g. `pydoc3 +gwinc.nb.BudgetItem`) + + +<!-- ## comparison with MATLAB gwinc --> + +<!-- `pygwinc` includes the ability use MATLAB gwinc directly via the --> +<!-- MATLAB python interface (see the CLI '--matlab' option above). This --> +<!-- also allows for easy direct comparison between the pygwinc and --> +<!-- matgwinc noise budgets. --> + +<!-- If you have a local checkout of matgwinc (at e.g. /path/to/gwinc) and --> +<!-- a local installation of MATLAB and it's python interface (at --> +<!-- e.g. /opt/matlab/python/lib/python3.6/site-packages) you can run the --> +<!-- comparison as so: --> +<!-- ```shell --> +<!-- $ export GWINCPATH=/path/to/matgwinc --> +<!-- $ export PYTHONPATH=/opt/matlab/python/lib/python3.6/site-packages --> +<!-- $ python3 -m gwinc.test -p aLIGO --> +<!-- ``` --> +<!-- This will produce a summary page of the various noise spectra that --> +<!-- differ between matgwinc and pygwinc. --> + +<!-- Latest comparison plots from continuous integration: --> + +<!-- * [aLIGO comparison](https://gwinc.docs.ligo.org/pygwinc/aLIGO_test.png) --> +<!-- * [A+ comparison](https://gwinc.docs.ligo.org/pygwinc/A+_test.png) -->