aspcap 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aspcap/__init__.py +8 -0
- aspcap/abundances.py +73 -0
- aspcap/cli.py +64 -0
- aspcap/continuum.py +437 -0
- aspcap/data/apogee-dr17-mgiant-full-weights.json +1 -0
- aspcap/data/apogee-dr17-mgiant-full.hdr +15 -0
- aspcap/data/apogee-dr17-mgiant-full.unf +0 -0
- aspcap/ferre.py +456 -0
- aspcap/spectrum.py +551 -0
- aspcap-0.2.0.dist-info/METADATA +185 -0
- aspcap-0.2.0.dist-info/RECORD +16 -0
- aspcap-0.2.0.dist-info/WHEEL +5 -0
- aspcap-0.2.0.dist-info/entry_points.txt +2 -0
- aspcap-0.2.0.dist-info/licenses/LICENSE +21 -0
- aspcap-0.2.0.dist-info/licenses/LICENSE-DOPPLER +21 -0
- aspcap-0.2.0.dist-info/top_level.txt +1 -0
aspcap/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Spectral data, external continuum normalization, and FERRE abundance fits."""
|
|
2
|
+
|
|
3
|
+
from .abundances import fit_abundance
|
|
4
|
+
from .ferre import Grid
|
|
5
|
+
from .spectrum import Spec1D
|
|
6
|
+
from . import continuum
|
|
7
|
+
|
|
8
|
+
__all__ = ["Grid", "fit_abundance", "Spec1D", "continuum"]
|
aspcap/abundances.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Fit an individual species using the continuum and labels of a global fit."""
|
|
2
|
+
|
|
3
|
+
from math import isclose, isfinite
|
|
4
|
+
import numpy as np
|
|
5
|
+
from .ferre import run_ferre
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def fit_abundance(grid, wavelength, flux, error, continuum, parameters, *,
|
|
9
|
+
star_id, species, label, windows, add_labels=(),
|
|
10
|
+
executable="ferre", keep_runs=False, run_root=None, mask=None):
|
|
11
|
+
"""Return a result row and a model on the native grid.
|
|
12
|
+
|
|
13
|
+
Inputs are in the grid's wavelength units and air/vacuum convention.
|
|
14
|
+
The optional mask uses True for unusable pixels.
|
|
15
|
+
Windows are inclusive (lower, upper) pairs with unit weights. The optional
|
|
16
|
+
add_labels are fixed global-fit coordinates added to report [X/H].
|
|
17
|
+
"""
|
|
18
|
+
npix = len(grid.wavelengths)
|
|
19
|
+
badmask = np.zeros(npix, bool) if mask is None else np.asarray(mask, dtype=bool)
|
|
20
|
+
if badmask.shape != (npix,):
|
|
21
|
+
raise ValueError("mask must have one Boolean value per grid pixel (True means bad)")
|
|
22
|
+
if any(len(values) != npix for values in (wavelength, flux, error, continuum)):
|
|
23
|
+
raise ValueError("Spectrum and continuum must have one row per grid pixel")
|
|
24
|
+
if not all(isclose(a, b, rel_tol=0, abs_tol=1e-4 * (grid.wavelengths[1] - grid.wavelengths[0]))
|
|
25
|
+
for a, b in zip(wavelength, grid.wavelengths)):
|
|
26
|
+
raise ValueError("Spectrum wavelengths do not match the native grid")
|
|
27
|
+
if label not in grid.labels:
|
|
28
|
+
raise ValueError(f"Unknown abundance label {label!r}. Available: {grid.labels}")
|
|
29
|
+
if (label in add_labels or len(set(add_labels)) != len(add_labels)
|
|
30
|
+
or any(name not in parameters or name not in grid.labels for name in add_labels)):
|
|
31
|
+
raise ValueError("Abundance conversion labels must be distinct fixed grid labels")
|
|
32
|
+
if not windows or any(not (isfinite(lo) and isfinite(hi) and lo <= hi)
|
|
33
|
+
for lo, hi in windows):
|
|
34
|
+
raise ValueError("Provide one or more finite wavelength windows (lower, upper)")
|
|
35
|
+
|
|
36
|
+
normalized_flux, normalized_error, weights = [], [], []
|
|
37
|
+
for wave, value, sigma, scale, bad in zip(wavelength, flux, error, continuum, badmask):
|
|
38
|
+
if not isfinite(scale) or scale <= 0:
|
|
39
|
+
raise ValueError("Global continuum values must be finite and positive")
|
|
40
|
+
value, sigma = value / scale, sigma / scale
|
|
41
|
+
good = not bad and isfinite(value) and isfinite(sigma) and sigma > 0
|
|
42
|
+
normalized_flux.append(value if good else 1.0)
|
|
43
|
+
normalized_error.append(sigma if good else 1e10)
|
|
44
|
+
weights.append(int(good and any(lo <= wave <= hi for lo, hi in windows)))
|
|
45
|
+
pixels_used = sum(weights)
|
|
46
|
+
if pixels_used < 2:
|
|
47
|
+
raise ValueError(f"{species}: fewer than two usable pixels in the element windows")
|
|
48
|
+
|
|
49
|
+
fit = run_ferre(grid, normalized_flux, normalized_error, parameters, label, weights,
|
|
50
|
+
executable=executable, keep_runs=keep_runs, run_root=run_root)
|
|
51
|
+
axis = grid.labels.index(label)
|
|
52
|
+
raw = fit.parameters[label]
|
|
53
|
+
low, step = grid.lower[axis], grid.step[axis]
|
|
54
|
+
high = low + (grid.counts[axis] - 1) * step
|
|
55
|
+
flags = []
|
|
56
|
+
if min(raw - low, high - raw) <= 0.125 * step:
|
|
57
|
+
flags.append("GRID_EDGE")
|
|
58
|
+
if fit.errors[label] is None:
|
|
59
|
+
flags.append("NO_UNCERTAINTY")
|
|
60
|
+
result = {
|
|
61
|
+
"star_id": star_id,
|
|
62
|
+
"species": species,
|
|
63
|
+
"grid_label": label,
|
|
64
|
+
"raw_value": raw,
|
|
65
|
+
"abundance_scale": f"[{species}/H]",
|
|
66
|
+
"abundance": raw + sum(parameters[name] for name in add_labels),
|
|
67
|
+
"conditional_error": fit.errors[label],
|
|
68
|
+
"ferre_reduced_chi2": fit.reduced_chi2,
|
|
69
|
+
"pixels_used": pixels_used,
|
|
70
|
+
"flags": "|".join(flags),
|
|
71
|
+
}
|
|
72
|
+
pixels = zip(wavelength, normalized_flux, normalized_error, fit.model, weights)
|
|
73
|
+
return result, pixels
|
aspcap/cli.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Command-line interface for a single HGE abundance fit."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import csv
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from . import Grid, fit_abundance
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _run(args):
|
|
12
|
+
path = args.config.resolve()
|
|
13
|
+
config = json.loads(path.read_text())
|
|
14
|
+
species = json.loads((path.parent / config["species_file"]).read_text())
|
|
15
|
+
grid = Grid.read(path.parent / config["grid"])
|
|
16
|
+
expected_frame = "vacuum" if grid.vacuum else "air"
|
|
17
|
+
if config["wavelength_frame"] != expected_frame:
|
|
18
|
+
raise ValueError(f"Grid uses {expected_frame} wavelengths")
|
|
19
|
+
with (path.parent / config["spectrum"]).open(newline="") as file:
|
|
20
|
+
rows = list(csv.DictReader(file))
|
|
21
|
+
columns = [[float(row[name]) for row in rows]
|
|
22
|
+
for name in ("wavelength", "flux", "error", "continuum")]
|
|
23
|
+
executable = args.ferre or config.get("ferre", "ferre")
|
|
24
|
+
if not args.ferre and "/" in executable:
|
|
25
|
+
executable = path.parent / executable
|
|
26
|
+
result, pixels = fit_abundance(
|
|
27
|
+
grid, *columns, config["parameters"], star_id=config["star_id"],
|
|
28
|
+
species=species["species"], label=species["label"], windows=species["windows"],
|
|
29
|
+
add_labels=species.get("add_labels", ()), executable=executable,
|
|
30
|
+
keep_runs=args.keep_runs, run_root=args.run_root,
|
|
31
|
+
)
|
|
32
|
+
if args.pixels:
|
|
33
|
+
with args.pixels.open("w", newline="") as file:
|
|
34
|
+
writer = csv.writer(file)
|
|
35
|
+
writer.writerow(("wavelength", "normalized_flux", "normalized_error", "model", "weight"))
|
|
36
|
+
writer.writerows(pixels)
|
|
37
|
+
with args.output.open("w", newline="") as file:
|
|
38
|
+
writer = csv.DictWriter(file, fieldnames=result)
|
|
39
|
+
writer.writeheader()
|
|
40
|
+
writer.writerow(result)
|
|
41
|
+
print(f"Wrote {args.output}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main():
|
|
45
|
+
parser = argparse.ArgumentParser(prog="aspcap", description=__doc__)
|
|
46
|
+
commands = parser.add_subparsers(dest="command", required=True)
|
|
47
|
+
abundances = commands.add_parser("abundances", help="fit an individual species")
|
|
48
|
+
abundances.add_argument("config", type=Path)
|
|
49
|
+
abundances.add_argument("--ferre", help="FERRE executable name or path")
|
|
50
|
+
abundances.add_argument("--output", type=Path, default=Path("abundance.csv"))
|
|
51
|
+
abundances.add_argument("--pixels", type=Path, help="save normalized data, model, and weights")
|
|
52
|
+
abundances.add_argument("--keep-runs", action="store_true")
|
|
53
|
+
abundances.add_argument("--run-root", type=Path, help="existing directory for FERRE run files")
|
|
54
|
+
args = parser.parse_args()
|
|
55
|
+
try:
|
|
56
|
+
if args.pixels and args.pixels.resolve() == args.output.resolve():
|
|
57
|
+
raise ValueError("--pixels and --output must use different files")
|
|
58
|
+
_run(args)
|
|
59
|
+
except (OSError, ValueError, KeyError, TypeError, RuntimeError) as error:
|
|
60
|
+
parser.exit(1, f"aspcap: {error}\n")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
main()
|
aspcap/continuum.py
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"""External, order-by-order continuum estimation. No FERRE calls are made.
|
|
2
|
+
|
|
3
|
+
Methods are deliberately explicit: polynomial/spline fits, a running median,
|
|
4
|
+
a binned percentile envelope, or a median correction to a data/model ratio.
|
|
5
|
+
These are configurable estimators, not claims of exact DR17 reproduction.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from copy import deepcopy
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
import numpy as np
|
|
11
|
+
from .spectrum import Spec1D
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _aligned(a, b):
|
|
15
|
+
return (
|
|
16
|
+
a is None
|
|
17
|
+
and b is None
|
|
18
|
+
or (a is not None and b is not None and np.array_equal(a, b, equal_nan=True))
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ContinuumResult:
|
|
24
|
+
"""Continuum estimate and diagnostics, independent of a spectrum object.
|
|
25
|
+
|
|
26
|
+
continuum : ndarray
|
|
27
|
+
Estimated divisor in the input flux units; dimensionless for model_ratio.
|
|
28
|
+
Invalid orders and padding contain NaN.
|
|
29
|
+
fit_mask : ndarray of bool
|
|
30
|
+
True where an original sample contributed to the estimate (opposite to
|
|
31
|
+
the spectrum's bad-pixel mask). Interpolated bad pixels are not fit data.
|
|
32
|
+
flags : tuple of tuples
|
|
33
|
+
Per-order quality flags. Empty tuples indicate successful estimation.
|
|
34
|
+
settings, parameters : dict, tuple
|
|
35
|
+
Method settings and per-order coefficients/support diagnostics.
|
|
36
|
+
|
|
37
|
+
Pixel errors rescaled by ``apply`` remain conditional on this continuum;
|
|
38
|
+
continuum-estimation uncertainty is not added to the error array.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
continuum: np.ndarray
|
|
42
|
+
fit_mask: np.ndarray
|
|
43
|
+
flags: tuple
|
|
44
|
+
settings: dict
|
|
45
|
+
parameters: tuple
|
|
46
|
+
_wave: object = field(repr=False)
|
|
47
|
+
_numpix: np.ndarray = field(repr=False)
|
|
48
|
+
|
|
49
|
+
def apply(self, spectrum, *, inplace=False):
|
|
50
|
+
"""Divide flux/errors by the same scale; copy by default.
|
|
51
|
+
|
|
52
|
+
Accepts Doppler-compatible objects and preserves their type. Invalid
|
|
53
|
+
scales remain masked with NaN flux/infinite errors. This method does not
|
|
54
|
+
change wavelength, bitmasks, or uncertainty floors. Repeated explicit
|
|
55
|
+
applications compose cached continuum factors and retain the raw data.
|
|
56
|
+
"""
|
|
57
|
+
data = Spec1D.from_spectrum(spectrum)
|
|
58
|
+
if (
|
|
59
|
+
data.shape != self.continuum.shape
|
|
60
|
+
or not _aligned(data.wave, self._wave)
|
|
61
|
+
or not np.array_equal(data.numpix, self._numpix)
|
|
62
|
+
):
|
|
63
|
+
raise ValueError(
|
|
64
|
+
"Continuum result does not match the spectrum wavelengths/order layout"
|
|
65
|
+
)
|
|
66
|
+
out = spectrum if inplace else deepcopy(spectrum)
|
|
67
|
+
scale = self.continuum
|
|
68
|
+
good_scale = np.isfinite(scale) & (scale > 0)
|
|
69
|
+
good = data.valid & good_scale
|
|
70
|
+
old = getattr(spectrum, "_cont", None) if data.normalized else None
|
|
71
|
+
if data.normalized and old is None:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
"Normalized input has no cached continuum for composing corrections"
|
|
74
|
+
)
|
|
75
|
+
flux = np.full(data.shape, np.nan)
|
|
76
|
+
np.divide(data.flux, scale, out=flux, where=good)
|
|
77
|
+
error = None
|
|
78
|
+
if data.err is not None:
|
|
79
|
+
error = np.full(data.shape, np.inf)
|
|
80
|
+
np.divide(data.err, scale, out=error, where=good)
|
|
81
|
+
if not data.normalized:
|
|
82
|
+
out._flux = data.flux.copy()
|
|
83
|
+
out._err = None if data.err is None else data.err.copy()
|
|
84
|
+
out._flux_unit = data.flux_unit
|
|
85
|
+
out.flux, out.err, out.mask = flux, error, ~good
|
|
86
|
+
out.cont = scale.copy() if old is None else np.asarray(old) * scale
|
|
87
|
+
out.normalized = True
|
|
88
|
+
out.flux_unit = (
|
|
89
|
+
data.flux_unit if self.settings["method"] == "model_ratio" else ""
|
|
90
|
+
)
|
|
91
|
+
history = deepcopy(getattr(out, "continuum_history", []))
|
|
92
|
+
history.append(
|
|
93
|
+
dict(settings=deepcopy(self.settings), flags=deepcopy(self.flags))
|
|
94
|
+
)
|
|
95
|
+
out.continuum_history = history
|
|
96
|
+
return out
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def from_array(spectrum, continuum):
|
|
100
|
+
"""Wrap a supplied/callback continuum without fitting or changing inputs."""
|
|
101
|
+
data = Spec1D.from_spectrum(spectrum)
|
|
102
|
+
array = np.asarray(continuum, dtype=float)
|
|
103
|
+
if array.shape != data.shape:
|
|
104
|
+
raise ValueError("Continuum must match the flux shape")
|
|
105
|
+
valid = np.isfinite(array) & (array > 0) & data.valid
|
|
106
|
+
flags = tuple(
|
|
107
|
+
() if np.any(Spec1D._column(valid, i)[:n]) else ("INVALID_CONTINUUM",)
|
|
108
|
+
for i, n in enumerate(data.numpix)
|
|
109
|
+
)
|
|
110
|
+
return ContinuumResult(
|
|
111
|
+
array.copy(),
|
|
112
|
+
valid,
|
|
113
|
+
flags,
|
|
114
|
+
{"method": "provided"},
|
|
115
|
+
tuple({} for _ in data.numpix),
|
|
116
|
+
None if data.wave is None else data.wave.copy(),
|
|
117
|
+
data.numpix.copy(),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def fit(
|
|
122
|
+
spectrum,
|
|
123
|
+
*,
|
|
124
|
+
method="chebyshev",
|
|
125
|
+
degree=None,
|
|
126
|
+
coordinate="pixel",
|
|
127
|
+
weighted=False,
|
|
128
|
+
clip=None,
|
|
129
|
+
max_iterations=5,
|
|
130
|
+
window=151,
|
|
131
|
+
percentile=90.0,
|
|
132
|
+
bins=20,
|
|
133
|
+
smoothing=None,
|
|
134
|
+
model=None,
|
|
135
|
+
ratio_bounds=(0.1, 10.0),
|
|
136
|
+
mode="mirror",
|
|
137
|
+
on_failure="raise",
|
|
138
|
+
):
|
|
139
|
+
"""Estimate a continuum independently in each spectral order.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
spectrum : Spec1D or Doppler-compatible object
|
|
144
|
+
Supplies flux, wave, optional err/mask, and optional numpix. Not modified.
|
|
145
|
+
method : {'chebyshev', 'polynomial', 'spline', 'median', 'percentile', 'model_ratio'}
|
|
146
|
+
Polynomial is an alias of Chebyshev (same polynomial space). Percentile
|
|
147
|
+
bins positive flux and linearly interpolates bin estimates, with constant
|
|
148
|
+
endpoints. Model_ratio smooths flux/model; both inputs must already use
|
|
149
|
+
comparable normalization, wavelength sampling, frame, and resolution.
|
|
150
|
+
degree : int, optional
|
|
151
|
+
Polynomial degree (default 4) or spline degree (default 3, range 1..5).
|
|
152
|
+
coordinate : {'pixel', 'wavelength'}
|
|
153
|
+
Fitting coordinate, rescaled per order. Pixel is suitable for APOGEE's
|
|
154
|
+
regular log-wavelength grid. Wavelength mode requires distinct samples.
|
|
155
|
+
weighted : bool
|
|
156
|
+
Inverse-variance polynomial/spline least squares; requires errors.
|
|
157
|
+
False by default. Filtering methods do not accept weighted fitting.
|
|
158
|
+
clip : (lower, upper), optional
|
|
159
|
+
Iterative residual rejection in robust MAD sigma units for polynomial or
|
|
160
|
+
spline fits only. Each side can be None. Disabled by default. This is
|
|
161
|
+
independent of FERRE's REJECTCONT; no FERRE setting is changed.
|
|
162
|
+
max_iterations : int
|
|
163
|
+
Maximum rejection/refit rounds after the initial fit.
|
|
164
|
+
window : odd int
|
|
165
|
+
Running filter width in pixels, for median/model_ratio (default 151).
|
|
166
|
+
percentile, bins : float, int
|
|
167
|
+
Percentile (0..100) and number of bins for the percentile estimator.
|
|
168
|
+
smoothing : nonnegative float, optional
|
|
169
|
+
Spline weighted residual sum bound. Default uses supplied errors, or a
|
|
170
|
+
robust adjacent-difference noise estimate; it scales with flux units.
|
|
171
|
+
model : array or spectrum object, optional
|
|
172
|
+
Required for model_ratio; same order layout. A spectrum's masks are used.
|
|
173
|
+
ratio_bounds : (float, float)
|
|
174
|
+
Positive allowed data/model ratio range before filtering.
|
|
175
|
+
mode : str
|
|
176
|
+
scipy.ndimage median-filter boundary mode; default mirror. Filtering
|
|
177
|
+
fills rejected positions by within-order linear interpolation first.
|
|
178
|
+
on_failure : {'raise', 'mask'}
|
|
179
|
+
Raise for insufficient support/nonpositive continuum, or return NaN for
|
|
180
|
+
that order with a quality flag. Never substitute a valid unity continuum.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
ContinuumResult
|
|
185
|
+
Divisor, original-sample fit mask, method parameters, and per-order flags.
|
|
186
|
+
Applying the result is a separate operation with copy-by-default behavior.
|
|
187
|
+
"""
|
|
188
|
+
data = Spec1D.from_spectrum(spectrum)
|
|
189
|
+
methods = (
|
|
190
|
+
"chebyshev",
|
|
191
|
+
"polynomial",
|
|
192
|
+
"spline",
|
|
193
|
+
"median",
|
|
194
|
+
"percentile",
|
|
195
|
+
"model_ratio",
|
|
196
|
+
)
|
|
197
|
+
if method not in methods:
|
|
198
|
+
raise ValueError(f"Unknown method {method!r}; choose from {methods}")
|
|
199
|
+
if coordinate not in ("pixel", "wavelength") or on_failure not in ("raise", "mask"):
|
|
200
|
+
raise ValueError("Invalid coordinate or on_failure setting")
|
|
201
|
+
fitted = method in ("chebyshev", "polynomial", "spline")
|
|
202
|
+
if weighted and (not fitted or data.err is None):
|
|
203
|
+
raise ValueError(
|
|
204
|
+
"Weighted fitting requires a polynomial/spline method and uncertainties"
|
|
205
|
+
)
|
|
206
|
+
if degree is None:
|
|
207
|
+
degree = 3 if method == "spline" else 4
|
|
208
|
+
if (
|
|
209
|
+
not isinstance(degree, (int, np.integer))
|
|
210
|
+
or degree < 0
|
|
211
|
+
or (method == "spline" and not 1 <= degree <= 5)
|
|
212
|
+
):
|
|
213
|
+
raise ValueError("Invalid fitting degree")
|
|
214
|
+
if not isinstance(max_iterations, (int, np.integer)) or max_iterations < 0:
|
|
215
|
+
raise ValueError("max_iterations must be a nonnegative integer")
|
|
216
|
+
if clip is not None:
|
|
217
|
+
if (
|
|
218
|
+
not fitted
|
|
219
|
+
or len(clip) != 2
|
|
220
|
+
or any(v is not None and (not np.isfinite(v) or v <= 0) for v in clip)
|
|
221
|
+
):
|
|
222
|
+
raise ValueError(
|
|
223
|
+
"clip requires a fit method and positive lower/upper limits or None"
|
|
224
|
+
)
|
|
225
|
+
if method in ("median", "model_ratio"):
|
|
226
|
+
if not isinstance(window, (int, np.integer)) or window < 1 or window % 2 != 1:
|
|
227
|
+
raise ValueError("window must be a positive odd number of pixels")
|
|
228
|
+
if mode not in ("reflect", "constant", "nearest", "mirror", "wrap"):
|
|
229
|
+
raise ValueError("Invalid median-filter boundary mode")
|
|
230
|
+
if method == "percentile" and (
|
|
231
|
+
not np.isfinite(percentile)
|
|
232
|
+
or not 0 <= percentile <= 100
|
|
233
|
+
or not isinstance(bins, (int, np.integer))
|
|
234
|
+
or bins < 2
|
|
235
|
+
):
|
|
236
|
+
raise ValueError("percentile must be 0..100 and bins an integer >=2")
|
|
237
|
+
if smoothing is not None and (not np.isfinite(smoothing) or smoothing < 0):
|
|
238
|
+
raise ValueError("smoothing must be finite and nonnegative")
|
|
239
|
+
if coordinate == "wavelength" and data.wave is None:
|
|
240
|
+
raise ValueError("Wavelength-coordinate fitting requires wavelengths")
|
|
241
|
+
model_data = None
|
|
242
|
+
model_flux = None
|
|
243
|
+
if method == "model_ratio":
|
|
244
|
+
if model is None:
|
|
245
|
+
raise ValueError("model_ratio requires a reference model")
|
|
246
|
+
if (
|
|
247
|
+
len(ratio_bounds) != 2
|
|
248
|
+
or not np.isfinite(ratio_bounds).all()
|
|
249
|
+
or not 0 < ratio_bounds[0] < ratio_bounds[1]
|
|
250
|
+
):
|
|
251
|
+
raise ValueError("Invalid ratio bounds")
|
|
252
|
+
if hasattr(model, "flux"):
|
|
253
|
+
model_data = Spec1D.from_spectrum(model)
|
|
254
|
+
if not _aligned(data.wave, model_data.wave) or not np.array_equal(
|
|
255
|
+
data.numpix, model_data.numpix
|
|
256
|
+
):
|
|
257
|
+
raise ValueError(
|
|
258
|
+
"Reference model wavelengths/order layout do not match"
|
|
259
|
+
)
|
|
260
|
+
if (
|
|
261
|
+
data.wavevac != model_data.wavevac
|
|
262
|
+
or data.wave_unit != model_data.wave_unit
|
|
263
|
+
):
|
|
264
|
+
raise ValueError(
|
|
265
|
+
"Reference model wavelength convention/units do not match"
|
|
266
|
+
)
|
|
267
|
+
if (
|
|
268
|
+
data.flux_unit is not None
|
|
269
|
+
and model_data.flux_unit is not None
|
|
270
|
+
and data.flux_unit != model_data.flux_unit
|
|
271
|
+
):
|
|
272
|
+
raise ValueError("Reference model flux units do not match")
|
|
273
|
+
model_flux = model_data.flux
|
|
274
|
+
else:
|
|
275
|
+
model_flux = np.asarray(model, dtype=float)
|
|
276
|
+
if model_flux.shape != data.shape:
|
|
277
|
+
raise ValueError("Reference model must match the flux shape")
|
|
278
|
+
continuum = np.full(data.shape, np.nan)
|
|
279
|
+
used = np.zeros(data.shape, bool)
|
|
280
|
+
flags, parameters = [], []
|
|
281
|
+
for order, count in enumerate(data.numpix):
|
|
282
|
+
y = Spec1D._column(data.flux, order)[:count].copy()
|
|
283
|
+
good = Spec1D._column(data.valid, order)[:count].copy()
|
|
284
|
+
errors = None if data.err is None else Spec1D._column(data.err, order)[:count]
|
|
285
|
+
x = (
|
|
286
|
+
np.arange(count, dtype=float)
|
|
287
|
+
if coordinate == "pixel"
|
|
288
|
+
else Spec1D._column(data.wave, order)[:count].copy()
|
|
289
|
+
)
|
|
290
|
+
good &= np.isfinite(x) & np.isfinite(y)
|
|
291
|
+
if method == "model_ratio":
|
|
292
|
+
m = Spec1D._column(model_flux, order)[:count]
|
|
293
|
+
valid_model = np.isfinite(m) & (m > 0)
|
|
294
|
+
if model_data is not None:
|
|
295
|
+
valid_model &= Spec1D._column(model_data.valid, order)[:count]
|
|
296
|
+
ratio = np.full(count, np.nan)
|
|
297
|
+
np.divide(y, m, out=ratio, where=valid_model)
|
|
298
|
+
y = ratio
|
|
299
|
+
good &= valid_model & (y >= ratio_bounds[0]) & (y <= ratio_bounds[1])
|
|
300
|
+
if method == "percentile":
|
|
301
|
+
good &= y > 0
|
|
302
|
+
try:
|
|
303
|
+
minimum = degree + 1 if fitted else 2
|
|
304
|
+
if np.sum(good) < minimum:
|
|
305
|
+
raise ValueError("INSUFFICIENT_PIXELS")
|
|
306
|
+
xmin, xmax = np.min(x[good]), np.max(x[good])
|
|
307
|
+
z = np.zeros(count) if xmax == xmin else 2 * (x - xmin) / (xmax - xmin) - 1
|
|
308
|
+
details = {}
|
|
309
|
+
for iteration in range((max_iterations if clip else 0) + 1):
|
|
310
|
+
if np.sum(good) < minimum:
|
|
311
|
+
raise ValueError("INSUFFICIENT_PIXELS")
|
|
312
|
+
if method in ("chebyshev", "polynomial"):
|
|
313
|
+
matrix = np.polynomial.chebyshev.chebvander(z[good], degree)
|
|
314
|
+
rhs = y[good]
|
|
315
|
+
if weighted:
|
|
316
|
+
matrix = matrix / errors[good, None]
|
|
317
|
+
rhs = rhs / errors[good]
|
|
318
|
+
coef, _, rank, _ = np.linalg.lstsq(matrix, rhs, rcond=None)
|
|
319
|
+
if rank < degree + 1:
|
|
320
|
+
raise ValueError("SINGULAR_FIT")
|
|
321
|
+
curve = np.polynomial.chebyshev.chebval(z, coef)
|
|
322
|
+
details = dict(
|
|
323
|
+
coefficients=coef.tolist(), domain=[float(xmin), float(xmax)]
|
|
324
|
+
)
|
|
325
|
+
elif method == "spline":
|
|
326
|
+
from scipy.interpolate import UnivariateSpline
|
|
327
|
+
|
|
328
|
+
select = np.flatnonzero(good)
|
|
329
|
+
select = select[np.argsort(z[select])]
|
|
330
|
+
if np.any(np.diff(z[select]) <= 0):
|
|
331
|
+
raise ValueError("DUPLICATE_COORDINATES")
|
|
332
|
+
bound = smoothing
|
|
333
|
+
if bound is None:
|
|
334
|
+
if weighted:
|
|
335
|
+
bound = len(select)
|
|
336
|
+
elif errors is not None:
|
|
337
|
+
bound = float(np.sum(errors[select] ** 2))
|
|
338
|
+
else:
|
|
339
|
+
diff = np.diff(y[select])
|
|
340
|
+
noise = (
|
|
341
|
+
1.4826
|
|
342
|
+
* np.median(np.abs(diff - np.median(diff)))
|
|
343
|
+
/ np.sqrt(2)
|
|
344
|
+
)
|
|
345
|
+
bound = len(select) * noise**2
|
|
346
|
+
spline = UnivariateSpline(
|
|
347
|
+
z[select],
|
|
348
|
+
y[select],
|
|
349
|
+
w=1 / errors[select] if weighted else None,
|
|
350
|
+
k=degree,
|
|
351
|
+
s=bound,
|
|
352
|
+
ext=0,
|
|
353
|
+
)
|
|
354
|
+
curve = spline(z)
|
|
355
|
+
details = dict(
|
|
356
|
+
knots=spline.get_knots().tolist(),
|
|
357
|
+
coefficients=spline.get_coeffs().tolist(),
|
|
358
|
+
domain=[float(xmin), float(xmax)],
|
|
359
|
+
smoothing=float(bound),
|
|
360
|
+
)
|
|
361
|
+
elif method == "percentile":
|
|
362
|
+
edges = np.linspace(xmin, xmax, bins + 1)
|
|
363
|
+
groups = np.clip(
|
|
364
|
+
np.searchsorted(edges, x[good], side="right") - 1, 0, bins - 1
|
|
365
|
+
)
|
|
366
|
+
centres, levels = [], []
|
|
367
|
+
for group in np.unique(groups):
|
|
368
|
+
sel = groups == group
|
|
369
|
+
centres.append(float(np.mean(x[good][sel])))
|
|
370
|
+
levels.append(float(np.percentile(y[good][sel], percentile)))
|
|
371
|
+
if len(centres) < 2:
|
|
372
|
+
raise ValueError("INSUFFICIENT_BINS")
|
|
373
|
+
curve = np.interp(x, centres, levels)
|
|
374
|
+
details = dict(bin_coordinates=centres, bin_levels=levels)
|
|
375
|
+
else:
|
|
376
|
+
from scipy.ndimage import median_filter
|
|
377
|
+
|
|
378
|
+
# Filtering is in pixel space even when fitting coordinates
|
|
379
|
+
# are wavelengths; there is never interpolation across orders.
|
|
380
|
+
pixels = np.arange(count)
|
|
381
|
+
filled = np.interp(pixels, pixels[good], y[good])
|
|
382
|
+
curve = median_filter(filled, size=window, mode=mode)
|
|
383
|
+
if clip is None or iteration == max_iterations:
|
|
384
|
+
break
|
|
385
|
+
residual = y - curve
|
|
386
|
+
centre = np.median(residual[good])
|
|
387
|
+
sigma = max(
|
|
388
|
+
1.4826 * np.median(np.abs(residual[good] - centre)),
|
|
389
|
+
32 * np.finfo(float).eps * max(1.0, float(np.max(abs(y[good])))),
|
|
390
|
+
)
|
|
391
|
+
keep = good.copy()
|
|
392
|
+
if clip[0] is not None:
|
|
393
|
+
keep &= residual - centre >= -clip[0] * sigma
|
|
394
|
+
if clip[1] is not None:
|
|
395
|
+
keep &= residual - centre <= clip[1] * sigma
|
|
396
|
+
if np.array_equal(keep, good):
|
|
397
|
+
break
|
|
398
|
+
good = keep
|
|
399
|
+
if not np.all(np.isfinite(curve[good])) or np.any(curve[good] <= 0):
|
|
400
|
+
raise ValueError("INVALID_CONTINUUM")
|
|
401
|
+
# Masked samples cannot become fit data, but a valid polynomial may
|
|
402
|
+
# still be evaluated at their positions. Invalid scales stay NaN.
|
|
403
|
+
curve[~np.isfinite(curve) | (curve <= 0)] = np.nan
|
|
404
|
+
Spec1D._column(continuum, order)[:count] = curve
|
|
405
|
+
Spec1D._column(used, order)[:count] = good
|
|
406
|
+
details["pixels_used"] = int(sum(good))
|
|
407
|
+
flags.append(())
|
|
408
|
+
parameters.append(details)
|
|
409
|
+
except (ValueError, np.linalg.LinAlgError) as error:
|
|
410
|
+
if on_failure == "raise":
|
|
411
|
+
raise ValueError(f"Order {order}: {error}") from error
|
|
412
|
+
flags.append((str(error),))
|
|
413
|
+
parameters.append({"pixels_used": int(sum(good))})
|
|
414
|
+
settings = dict(
|
|
415
|
+
method=method,
|
|
416
|
+
degree=degree,
|
|
417
|
+
coordinate=coordinate,
|
|
418
|
+
weighted=weighted,
|
|
419
|
+
clip=clip,
|
|
420
|
+
max_iterations=max_iterations,
|
|
421
|
+
window=window,
|
|
422
|
+
percentile=percentile,
|
|
423
|
+
bins=bins,
|
|
424
|
+
smoothing=smoothing,
|
|
425
|
+
ratio_bounds=ratio_bounds if method == "model_ratio" else None,
|
|
426
|
+
mode=mode,
|
|
427
|
+
on_failure=on_failure,
|
|
428
|
+
)
|
|
429
|
+
return ContinuumResult(
|
|
430
|
+
continuum,
|
|
431
|
+
used,
|
|
432
|
+
tuple(flags),
|
|
433
|
+
settings,
|
|
434
|
+
tuple(parameters),
|
|
435
|
+
None if data.wave is None else data.wave.copy(),
|
|
436
|
+
data.numpix.copy(),
|
|
437
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
&SYNTH
|
|
2
|
+
ID = 'APOGEE DR17 M giant 5x5x5'
|
|
3
|
+
N_OF_DIM = 3
|
|
4
|
+
N_P = 5 5 5
|
|
5
|
+
LABEL(1) = 'METALS'
|
|
6
|
+
LABEL(2) = 'LOGG'
|
|
7
|
+
LABEL(3) = 'TEFF'
|
|
8
|
+
LLIMITS = -1.0 0.0 3400
|
|
9
|
+
STEPS = 0.25 0.5 100
|
|
10
|
+
NPIX = 8575
|
|
11
|
+
WAVE = 4.179000 0.000006
|
|
12
|
+
LOGW = 1
|
|
13
|
+
VACUUM = 1
|
|
14
|
+
RESOLUTION = 22500
|
|
15
|
+
/
|
|
Binary file
|