diskmelts 2.1__tar.gz → 2.1.1__tar.gz
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.
- {diskmelts-2.1/src/diskmelts.egg-info → diskmelts-2.1.1}/PKG-INFO +38 -57
- {diskmelts-2.1 → diskmelts-2.1.1}/README.md +37 -56
- {diskmelts-2.1 → diskmelts-2.1.1}/pyproject.toml +1 -1
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/fitting.py +24 -9
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/plotting.py +2 -1
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/v2_grid_fitting.py +18 -14
- {diskmelts-2.1 → diskmelts-2.1.1/src/diskmelts.egg-info}/PKG-INFO +38 -57
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_dev_v2_fitting_grids.py +39 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_shared_area_fitting.py +38 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/LICENSE +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/setup.cfg +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/__init__.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/__init__.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/_api.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/_core.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/cont_ranges.csv +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/cont_ranges_PPan.csv +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/carbon_extended.csv +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/carbon_extended.json +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/carbon_rich.csv +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/carbon_rich.json +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/ttauri.csv +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/ttauri.json +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/water.csv +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/consub/regions/water.json +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/13C12CH2_single_v2/net_13C12CH2_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/13CO2_single_v2/net_13CO2_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/C2H2_single_v2/net_C2H2_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/C2H4_single_v2/net_C2H4_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/C2H6_single_v2/net_C2H6_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/C2HD_single_v2/net_C2HD_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/C4H2_single_v2/net_C4H2_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/CH4_single_v2/net_CH4_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/CO17O_single_v2/net_CO17O_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/CO18O_single_v2/net_CO18O_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/CO2_single_v2/net_CO2_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/H2O_wavelength_bank_v2_fixed/H2O_model_bank.json +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/H2O_wavelength_bank_v2_fixed/net_H2O_w13p5to19p2_n13to19_t100to1400.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/H2O_wavelength_bank_v2_fixed/net_H2O_w19to25_n13to19_t100to1400.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/H2O_wavelength_bank_v2_fixed/net_H2O_w4p9to7p4_n13to19_t100to1400.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/H2O_wavelength_bank_v2_fixed/net_H2O_w7p2to11p2_n13to19_t100to1400.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/H2O_wavelength_bank_v2_fixed/net_H2O_w9p9to15p1_n13to19_t100to1400.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/HC3N_single_v2/net_HC3N_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/models/Trained_model/HCN_single_v2/net_HCN_forward.pt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/trainmodel.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/v2_fitting.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/v2_paper_comparison.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/v2_read_plot.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts/validation.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts.egg-info/SOURCES.txt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts.egg-info/dependency_links.txt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts.egg-info/requires.txt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/src/diskmelts.egg-info/top_level.txt +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_consub_package.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_detection.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_dev_v2_fitting_distance.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_dev_v2_read_plot.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_imports.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_io.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_mlp.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_model_bank.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_smoke.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_v2_package_assets.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_water_training.py +0 -0
- {diskmelts-2.1 → diskmelts-2.1.1}/tests/test_workflows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diskmelts
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Machine-learning Enabled Line fitting Tool for Spectra of protoplanetary disks
|
|
5
5
|
Author: Chengyan Xie, Dingshan Deng
|
|
6
6
|
License-Expression: MIT
|
|
@@ -49,58 +49,29 @@ density (`logN`), and emitting area scaling (`A`) from observed spectra.
|
|
|
49
49
|
|
|
50
50
|
## Installation
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
it from any directory
|
|
52
|
+
DiskMELTS 2.1.1 is available on [PyPI](https://pypi.org/project/diskmelts/).
|
|
53
|
+
With Python 3.9 or newer, install it from any directory. These commands use
|
|
54
|
+
`python` in an activated environment; the
|
|
55
|
+
[installation guide](https://diskmelts.readthedocs.io/en/latest/installation.html)
|
|
56
|
+
shows how to create one on macOS, Linux, Windows, or Conda:
|
|
54
57
|
|
|
55
58
|
```bash
|
|
56
59
|
python -m pip install diskmelts
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Until the first PyPI release, install directly from GitHub without a clone:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
python -m pip install "diskmelts[io] @ git+https://github.com/wddlx/DiskMELTS.git"
|
|
60
|
+
python -c "import diskmelts; print('DiskMELTS is ready')"
|
|
64
61
|
```
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
Full slab grids for training and direct-grid fitting are separate downloads;
|
|
68
|
-
pass their local path as `grid_root`. See the
|
|
69
|
-
[installation guide](https://diskmelts.readthedocs.io/en/latest/installation.html)
|
|
70
|
-
for details.
|
|
71
|
-
|
|
72
|
-
### Development checkout (macOS, Linux, Windows)
|
|
73
|
-
|
|
74
|
-
From the repository root, use a native Python 3.10 or 3.11 interpreter to
|
|
75
|
-
create a virtual environment. For FITS input and the example notebooks:
|
|
63
|
+
Optional features can be installed in the same way:
|
|
76
64
|
|
|
77
65
|
```bash
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.venv/bin/python -c "import diskmelts; print(diskmelts.__name__)"
|
|
66
|
+
python -m pip install "diskmelts[io]" # FITS input
|
|
67
|
+
python -m pip install "diskmelts[io,notebooks]" # FITS input and JupyterLab
|
|
81
68
|
```
|
|
82
69
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### Conda or Mamba
|
|
89
|
-
|
|
90
|
-
The YAML files use Conda only for Python and pip; pip installs the package and
|
|
91
|
-
its declared dependencies. Choose one from the repository root:
|
|
92
|
-
|
|
93
|
-
| File | Contents |
|
|
94
|
-
| --- | --- |
|
|
95
|
-
| `environment.yaml` | Core package |
|
|
96
|
-
| `environment-notebooks.yaml` | Core package, FITS support, JupyterLab |
|
|
97
|
-
| `environment-dev.yaml` | Notebook environment, tests, documentation tools |
|
|
98
|
-
|
|
99
|
-
For the notebook setup, run `mamba env create --file environment-notebooks.yaml`
|
|
100
|
-
then `conda activate diskmelts-notebooks`. `conda env create --file` works with
|
|
101
|
-
the same files when using a current Conda release. On Apple Silicon, use a
|
|
102
|
-
native `osx-arm64` installation such as Miniforge; an older Intel Conda can
|
|
103
|
-
still spend a long time solving even these smaller files.
|
|
70
|
+
The pip package includes the production checkpoints and continuum reference
|
|
71
|
+
tables. Full slab grids for training and direct-grid fitting are separate
|
|
72
|
+
downloads; pass their local path as `grid_root`.
|
|
73
|
+
Example notebooks and spectra can be downloaded from the repository when
|
|
74
|
+
needed; they are not installed by pip.
|
|
104
75
|
|
|
105
76
|
## Runtime dependencies
|
|
106
77
|
|
|
@@ -158,34 +129,44 @@ special regions, FITS input, saving CSVs, and uncertainty diagnostics.
|
|
|
158
129
|
|
|
159
130
|
## Quick fitting example
|
|
160
131
|
|
|
132
|
+
Use a continuum-subtracted CSV with a header and `wave,flux` as its first two
|
|
133
|
+
columns, with wavelength in µm and flux in Jy. For a runnable example without
|
|
134
|
+
cloning the repository, download the
|
|
135
|
+
[J16120505 spectrum](https://github.com/wddlx/DiskMELTS/blob/main/Realobs_data/Consub_data/j16120505_v9.0_contsub_RVcorr.csv)
|
|
136
|
+
as `j16120505_contsub.csv`, or run:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
curl -L -o j16120505_contsub.csv https://raw.githubusercontent.com/wddlx/DiskMELTS/main/Realobs_data/Consub_data/j16120505_v9.0_contsub_RVcorr.csv
|
|
140
|
+
```
|
|
141
|
+
|
|
161
142
|
```python
|
|
162
|
-
from diskmelts import load_fitting_models, load_observed_spectrum
|
|
143
|
+
from diskmelts import fit_stage, load_fitting_models, load_observed_spectrum
|
|
163
144
|
|
|
164
|
-
|
|
145
|
+
models = load_fitting_models('C2H2')
|
|
165
146
|
|
|
166
147
|
obs_wav, obs_flux = load_observed_spectrum(
|
|
167
|
-
'
|
|
148
|
+
'j16120505_contsub.csv'
|
|
168
149
|
)
|
|
169
150
|
|
|
170
151
|
fit = fit_stage(
|
|
171
152
|
obs_wav,
|
|
172
153
|
obs_flux,
|
|
173
|
-
molecule='
|
|
174
|
-
wavelength_range=(
|
|
175
|
-
pretrained=
|
|
176
|
-
fit_ranges=
|
|
154
|
+
molecule='C2H2',
|
|
155
|
+
wavelength_range=(13.0, 15.0),
|
|
156
|
+
pretrained=models,
|
|
157
|
+
fit_ranges=(13.5, 14.1),
|
|
177
158
|
distance_pc=122.5,
|
|
178
|
-
n_samples=
|
|
179
|
-
n_refine=
|
|
180
|
-
n_top=
|
|
181
|
-
sigma=0.001,
|
|
159
|
+
n_samples=1024,
|
|
160
|
+
n_refine=4,
|
|
161
|
+
n_top=4,
|
|
182
162
|
)
|
|
183
163
|
|
|
184
|
-
print(fit['params'])
|
|
164
|
+
print(fit['params']['C2H2'])
|
|
185
165
|
```
|
|
186
166
|
|
|
187
167
|
You can also read spectra with your own code. DiskMELTS only requires
|
|
188
|
-
one-dimensional `obs_wav` and `obs_flux` arrays.
|
|
168
|
+
one-dimensional `obs_wav` and `obs_flux` arrays. Replace the sample CSV with
|
|
169
|
+
your own file to fit your source; update `distance_pc` and fit intervals.
|
|
189
170
|
|
|
190
171
|
For a complete staged fit-and-subtract workflow from a checkout, edit and run:
|
|
191
172
|
|
|
@@ -10,58 +10,29 @@ density (`logN`), and emitting area scaling (`A`) from observed spectra.
|
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
it from any directory
|
|
13
|
+
DiskMELTS 2.1.1 is available on [PyPI](https://pypi.org/project/diskmelts/).
|
|
14
|
+
With Python 3.9 or newer, install it from any directory. These commands use
|
|
15
|
+
`python` in an activated environment; the
|
|
16
|
+
[installation guide](https://diskmelts.readthedocs.io/en/latest/installation.html)
|
|
17
|
+
shows how to create one on macOS, Linux, Windows, or Conda:
|
|
15
18
|
|
|
16
19
|
```bash
|
|
17
20
|
python -m pip install diskmelts
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Until the first PyPI release, install directly from GitHub without a clone:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
python -m pip install "diskmelts[io] @ git+https://github.com/wddlx/DiskMELTS.git"
|
|
21
|
+
python -c "import diskmelts; print('DiskMELTS is ready')"
|
|
25
22
|
```
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
Full slab grids for training and direct-grid fitting are separate downloads;
|
|
29
|
-
pass their local path as `grid_root`. See the
|
|
30
|
-
[installation guide](https://diskmelts.readthedocs.io/en/latest/installation.html)
|
|
31
|
-
for details.
|
|
32
|
-
|
|
33
|
-
### Development checkout (macOS, Linux, Windows)
|
|
34
|
-
|
|
35
|
-
From the repository root, use a native Python 3.10 or 3.11 interpreter to
|
|
36
|
-
create a virtual environment. For FITS input and the example notebooks:
|
|
24
|
+
Optional features can be installed in the same way:
|
|
37
25
|
|
|
38
26
|
```bash
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.venv/bin/python -c "import diskmelts; print(diskmelts.__name__)"
|
|
27
|
+
python -m pip install "diskmelts[io]" # FITS input
|
|
28
|
+
python -m pip install "diskmelts[io,notebooks]" # FITS input and JupyterLab
|
|
42
29
|
```
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### Conda or Mamba
|
|
50
|
-
|
|
51
|
-
The YAML files use Conda only for Python and pip; pip installs the package and
|
|
52
|
-
its declared dependencies. Choose one from the repository root:
|
|
53
|
-
|
|
54
|
-
| File | Contents |
|
|
55
|
-
| --- | --- |
|
|
56
|
-
| `environment.yaml` | Core package |
|
|
57
|
-
| `environment-notebooks.yaml` | Core package, FITS support, JupyterLab |
|
|
58
|
-
| `environment-dev.yaml` | Notebook environment, tests, documentation tools |
|
|
59
|
-
|
|
60
|
-
For the notebook setup, run `mamba env create --file environment-notebooks.yaml`
|
|
61
|
-
then `conda activate diskmelts-notebooks`. `conda env create --file` works with
|
|
62
|
-
the same files when using a current Conda release. On Apple Silicon, use a
|
|
63
|
-
native `osx-arm64` installation such as Miniforge; an older Intel Conda can
|
|
64
|
-
still spend a long time solving even these smaller files.
|
|
31
|
+
The pip package includes the production checkpoints and continuum reference
|
|
32
|
+
tables. Full slab grids for training and direct-grid fitting are separate
|
|
33
|
+
downloads; pass their local path as `grid_root`.
|
|
34
|
+
Example notebooks and spectra can be downloaded from the repository when
|
|
35
|
+
needed; they are not installed by pip.
|
|
65
36
|
|
|
66
37
|
## Runtime dependencies
|
|
67
38
|
|
|
@@ -119,34 +90,44 @@ special regions, FITS input, saving CSVs, and uncertainty diagnostics.
|
|
|
119
90
|
|
|
120
91
|
## Quick fitting example
|
|
121
92
|
|
|
93
|
+
Use a continuum-subtracted CSV with a header and `wave,flux` as its first two
|
|
94
|
+
columns, with wavelength in µm and flux in Jy. For a runnable example without
|
|
95
|
+
cloning the repository, download the
|
|
96
|
+
[J16120505 spectrum](https://github.com/wddlx/DiskMELTS/blob/main/Realobs_data/Consub_data/j16120505_v9.0_contsub_RVcorr.csv)
|
|
97
|
+
as `j16120505_contsub.csv`, or run:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
curl -L -o j16120505_contsub.csv https://raw.githubusercontent.com/wddlx/DiskMELTS/main/Realobs_data/Consub_data/j16120505_v9.0_contsub_RVcorr.csv
|
|
101
|
+
```
|
|
102
|
+
|
|
122
103
|
```python
|
|
123
|
-
from diskmelts import load_fitting_models, load_observed_spectrum
|
|
104
|
+
from diskmelts import fit_stage, load_fitting_models, load_observed_spectrum
|
|
124
105
|
|
|
125
|
-
|
|
106
|
+
models = load_fitting_models('C2H2')
|
|
126
107
|
|
|
127
108
|
obs_wav, obs_flux = load_observed_spectrum(
|
|
128
|
-
'
|
|
109
|
+
'j16120505_contsub.csv'
|
|
129
110
|
)
|
|
130
111
|
|
|
131
112
|
fit = fit_stage(
|
|
132
113
|
obs_wav,
|
|
133
114
|
obs_flux,
|
|
134
|
-
molecule='
|
|
135
|
-
wavelength_range=(
|
|
136
|
-
pretrained=
|
|
137
|
-
fit_ranges=
|
|
115
|
+
molecule='C2H2',
|
|
116
|
+
wavelength_range=(13.0, 15.0),
|
|
117
|
+
pretrained=models,
|
|
118
|
+
fit_ranges=(13.5, 14.1),
|
|
138
119
|
distance_pc=122.5,
|
|
139
|
-
n_samples=
|
|
140
|
-
n_refine=
|
|
141
|
-
n_top=
|
|
142
|
-
sigma=0.001,
|
|
120
|
+
n_samples=1024,
|
|
121
|
+
n_refine=4,
|
|
122
|
+
n_top=4,
|
|
143
123
|
)
|
|
144
124
|
|
|
145
|
-
print(fit['params'])
|
|
125
|
+
print(fit['params']['C2H2'])
|
|
146
126
|
```
|
|
147
127
|
|
|
148
128
|
You can also read spectra with your own code. DiskMELTS only requires
|
|
149
|
-
one-dimensional `obs_wav` and `obs_flux` arrays.
|
|
129
|
+
one-dimensional `obs_wav` and `obs_flux` arrays. Replace the sample CSV with
|
|
130
|
+
your own file to fit your source; update `distance_pc` and fit intervals.
|
|
150
131
|
|
|
151
132
|
For a complete staged fit-and-subtract workflow from a checkout, edit and run:
|
|
152
133
|
|
|
@@ -519,6 +519,9 @@ def _build_components(molecules, h2o_components=1, component_names=None):
|
|
|
519
519
|
raise ValueError('component_names must match the number of fitted components')
|
|
520
520
|
for comp, label in zip(components, component_names):
|
|
521
521
|
comp['label'] = label
|
|
522
|
+
labels = [comp['label'] for comp in components]
|
|
523
|
+
if len(labels) != len(set(labels)):
|
|
524
|
+
raise ValueError('component labels must be unique; name repeated molecules with component_names')
|
|
522
525
|
return components
|
|
523
526
|
|
|
524
527
|
|
|
@@ -697,8 +700,9 @@ def fit_molecules(
|
|
|
697
700
|
molecule names or component labels.
|
|
698
701
|
logN_bounds (tuple or dict or None): default (14, 19). A dict can use
|
|
699
702
|
molecule names or component labels.
|
|
700
|
-
loga_bounds (tuple or None): bounds on log10(A)
|
|
701
|
-
|
|
703
|
+
loga_bounds (tuple, dict, or None): bounds on log10(A), optionally
|
|
704
|
+
keyed by component label or molecule. None means A >= 0 with no
|
|
705
|
+
upper bound. Shared-area groups use the intersection of bounds.
|
|
702
706
|
shared_area_groups (list of sequences or None): component labels whose
|
|
703
707
|
emitting areas must be equal, e.g. [('C2H2', '13C12CH2')]. Each
|
|
704
708
|
component may appear in at most one group. Unlisted components
|
|
@@ -914,10 +918,22 @@ def fit_molecules(
|
|
|
914
918
|
)
|
|
915
919
|
return physical
|
|
916
920
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
+
component_area_bounds = []
|
|
922
|
+
for comp in components:
|
|
923
|
+
pair = _bounds_for(comp, loga_bounds, None)
|
|
924
|
+
if pair is None:
|
|
925
|
+
component_area_bounds.append((0.0, np.inf))
|
|
926
|
+
continue
|
|
927
|
+
if (len(pair) != 2 or not np.all(np.isfinite(pair)) or
|
|
928
|
+
pair[0] >= pair[1] or not 0 < 10.0 ** pair[0] < 10.0 ** pair[1]):
|
|
929
|
+
raise ValueError(f'{comp["label"]} needs finite increasing loga_bounds')
|
|
930
|
+
component_area_bounds.append((10.0 ** pair[0], 10.0 ** pair[1]))
|
|
931
|
+
area_lower = np.array([max(component_area_bounds[i][0] for i in members)
|
|
932
|
+
for members in area_group_members])
|
|
933
|
+
area_upper = np.array([min(component_area_bounds[i][1] for i in members)
|
|
934
|
+
for members in area_group_members])
|
|
935
|
+
if np.any(area_lower >= area_upper):
|
|
936
|
+
raise ValueError('shared area group has no overlap in loga_bounds')
|
|
921
937
|
|
|
922
938
|
def _matrix_for(params):
|
|
923
939
|
cols = []
|
|
@@ -939,12 +955,11 @@ def fit_molecules(
|
|
|
939
955
|
flux_scale = max(float(np.max(np.abs(obs_fit))),
|
|
940
956
|
float(np.max(np.abs(M_group))), np.finfo(float).tiny)
|
|
941
957
|
M_scaled, obs_scaled = M_group / flux_scale, obs_fit / flux_scale
|
|
942
|
-
if np.isfinite(
|
|
958
|
+
if np.any(area_lower > 0) or np.any(np.isfinite(area_upper)):
|
|
943
959
|
res = scipy.optimize.lsq_linear(
|
|
944
960
|
M_scaled,
|
|
945
961
|
obs_scaled,
|
|
946
|
-
bounds=(
|
|
947
|
-
np.full(len(area_group_members), A_bounds[1])),
|
|
962
|
+
bounds=(area_lower, area_upper),
|
|
948
963
|
)
|
|
949
964
|
A_group = np.asarray(res.x, dtype=np.float64)
|
|
950
965
|
else:
|
|
@@ -270,7 +270,8 @@ def plot_fit(
|
|
|
270
270
|
ax_bot.text(0.99, 0.92, rf'$\chi^2_{{\rm red}} = {chi2_red:.2f}$',
|
|
271
271
|
transform=ax_bot.transAxes, ha='right', va='top', fontsize=9)
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
fig.subplots_adjust(left=0.11, right=0.98, bottom=0.10, top=0.92,
|
|
274
|
+
hspace=0.08)
|
|
274
275
|
if show:
|
|
275
276
|
plt.show()
|
|
276
277
|
if save_path is not None:
|
|
@@ -58,8 +58,8 @@ def _stage_ranges(fit_ranges, components):
|
|
|
58
58
|
|
|
59
59
|
def _components(molecule, h2o_components, component_names):
|
|
60
60
|
molecules = [molecule] if isinstance(molecule, str) else list(molecule)
|
|
61
|
-
if not molecules
|
|
62
|
-
raise ValueError('molecule must contain
|
|
61
|
+
if not molecules:
|
|
62
|
+
raise ValueError('molecule must contain at least one molecular name')
|
|
63
63
|
if h2o_components not in (1, 2):
|
|
64
64
|
raise ValueError('h2o_components must be 1 or 2')
|
|
65
65
|
parts = []
|
|
@@ -281,10 +281,6 @@ def fit_grid_stage(obs_wav, obs_flux, molecule, wavelength_range, fit_ranges, *,
|
|
|
281
281
|
noise_scale = float(sigma) * to_reference if sigma is not None else max(
|
|
282
282
|
float(np.std(target)), np.finfo(float).tiny,
|
|
283
283
|
)
|
|
284
|
-
lo_A, hi_A = 10.0 ** float(loga_bounds[0]), 10.0 ** float(loga_bounds[1])
|
|
285
|
-
if not 0 < lo_A < hi_A:
|
|
286
|
-
raise ValueError('loga_bounds must be increasing and finite')
|
|
287
|
-
|
|
288
284
|
if n_refine < 1:
|
|
289
285
|
raise ValueError('n_refine must be positive')
|
|
290
286
|
|
|
@@ -301,6 +297,14 @@ def fit_grid_stage(obs_wav, obs_flux, molecule, wavelength_range, fit_ranges, *,
|
|
|
301
297
|
bounds = [(bound_for(T_bounds, part, (100.0, 1400.0)),
|
|
302
298
|
bound_for(logN_bounds, part, default_logN(part)))
|
|
303
299
|
for part in parts]
|
|
300
|
+
area_bounds = [bound_for(loga_bounds, part, (-2.0, 2.0))
|
|
301
|
+
for part in parts]
|
|
302
|
+
if any(len(pair) != 2 or not np.all(np.isfinite(pair)) or
|
|
303
|
+
pair[0] >= pair[1] or not 0 < 10.0 ** pair[0] < 10.0 ** pair[1]
|
|
304
|
+
for pair in area_bounds):
|
|
305
|
+
raise ValueError('loga_bounds must be increasing and finite')
|
|
306
|
+
area_lower = np.array([10.0 ** pair[0] for pair in area_bounds])
|
|
307
|
+
area_upper = np.array([10.0 ** pair[1] for pair in area_bounds])
|
|
304
308
|
molecules = list(dict.fromkeys(part['model_mol'] for part in parts))
|
|
305
309
|
libraries = {}
|
|
306
310
|
for mol in molecules:
|
|
@@ -330,7 +334,7 @@ def fit_grid_stage(obs_wav, obs_flux, molecule, wavelength_range, fit_ranges, *,
|
|
|
330
334
|
def profile_amplitudes(nonlin):
|
|
331
335
|
if (ordered_water and
|
|
332
336
|
nonlin[2 * water_indices[0]] > nonlin[2 * water_indices[1]]):
|
|
333
|
-
return np.inf,
|
|
337
|
+
return np.inf, area_lower.copy()
|
|
334
338
|
matrix = templates(nonlin)
|
|
335
339
|
scale = max(float(np.max(np.abs(target))),
|
|
336
340
|
float(np.max(np.abs(matrix))), np.finfo(float).tiny)
|
|
@@ -338,12 +342,12 @@ def fit_grid_stage(obs_wav, obs_flux, molecule, wavelength_range, fit_ranges, *,
|
|
|
338
342
|
column = matrix[:, 0] / scale
|
|
339
343
|
denom = float(np.dot(column, column))
|
|
340
344
|
amplitude = (float(np.clip(np.dot(column, target / scale) / denom,
|
|
341
|
-
|
|
345
|
+
area_lower[0], area_upper[0]))
|
|
346
|
+
if denom > 0 else area_lower[0])
|
|
342
347
|
amplitudes = np.array([amplitude])
|
|
343
348
|
else:
|
|
344
349
|
solution = lsq_linear(matrix / scale, target / scale,
|
|
345
|
-
bounds=(
|
|
346
|
-
np.full(n_parts, hi_A)))
|
|
350
|
+
bounds=(area_lower, area_upper))
|
|
347
351
|
amplitudes = solution.x
|
|
348
352
|
residual = matrix @ amplitudes - target
|
|
349
353
|
return float(np.dot(residual, residual)), amplitudes
|
|
@@ -358,8 +362,8 @@ def fit_grid_stage(obs_wav, obs_flux, molecule, wavelength_range, fit_ranges, *,
|
|
|
358
362
|
key=lambda x: profile_amplitudes(x)[0])
|
|
359
363
|
if not candidates:
|
|
360
364
|
raise RuntimeError('grid search found no feasible component ordering')
|
|
361
|
-
solution_bounds = [entry for pair in bounds
|
|
362
|
-
for entry in (pair[0], pair[1],
|
|
365
|
+
solution_bounds = [entry for k, pair in enumerate(bounds)
|
|
366
|
+
for entry in (pair[0], pair[1], area_bounds[k])]
|
|
363
367
|
lower = np.array([item[0] for item in solution_bounds], dtype=float)
|
|
364
368
|
upper = np.array([item[1] for item in solution_bounds], dtype=float)
|
|
365
369
|
scales = np.array([
|
|
@@ -445,8 +449,8 @@ def fit_grid_stage(obs_wav, obs_flux, molecule, wavelength_range, fit_ranges, *,
|
|
|
445
449
|
np.isclose(T, bounds[k][0][1], rtol=0, atol=1e-5)),
|
|
446
450
|
'logN_at_bound': bool(np.isclose(logN, bounds[k][1][0], rtol=0, atol=1e-6) or
|
|
447
451
|
np.isclose(logN, bounds[k][1][1], rtol=0, atol=1e-6)),
|
|
448
|
-
'log10A_at_bound': bool(np.isclose(logA,
|
|
449
|
-
|
|
452
|
+
'log10A_at_bound': bool(np.isclose(logA, area_bounds[k][0], rtol=0, atol=1e-6) or
|
|
453
|
+
np.isclose(logA, area_bounds[k][1], rtol=0, atol=1e-6)),
|
|
450
454
|
}
|
|
451
455
|
print(f' Grid fit: {len(parts)} components, weighted SSE={best_loss:.4g}')
|
|
452
456
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diskmelts
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Machine-learning Enabled Line fitting Tool for Spectra of protoplanetary disks
|
|
5
5
|
Author: Chengyan Xie, Dingshan Deng
|
|
6
6
|
License-Expression: MIT
|
|
@@ -49,58 +49,29 @@ density (`logN`), and emitting area scaling (`A`) from observed spectra.
|
|
|
49
49
|
|
|
50
50
|
## Installation
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
it from any directory
|
|
52
|
+
DiskMELTS 2.1.1 is available on [PyPI](https://pypi.org/project/diskmelts/).
|
|
53
|
+
With Python 3.9 or newer, install it from any directory. These commands use
|
|
54
|
+
`python` in an activated environment; the
|
|
55
|
+
[installation guide](https://diskmelts.readthedocs.io/en/latest/installation.html)
|
|
56
|
+
shows how to create one on macOS, Linux, Windows, or Conda:
|
|
54
57
|
|
|
55
58
|
```bash
|
|
56
59
|
python -m pip install diskmelts
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Until the first PyPI release, install directly from GitHub without a clone:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
python -m pip install "diskmelts[io] @ git+https://github.com/wddlx/DiskMELTS.git"
|
|
60
|
+
python -c "import diskmelts; print('DiskMELTS is ready')"
|
|
64
61
|
```
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
Full slab grids for training and direct-grid fitting are separate downloads;
|
|
68
|
-
pass their local path as `grid_root`. See the
|
|
69
|
-
[installation guide](https://diskmelts.readthedocs.io/en/latest/installation.html)
|
|
70
|
-
for details.
|
|
71
|
-
|
|
72
|
-
### Development checkout (macOS, Linux, Windows)
|
|
73
|
-
|
|
74
|
-
From the repository root, use a native Python 3.10 or 3.11 interpreter to
|
|
75
|
-
create a virtual environment. For FITS input and the example notebooks:
|
|
63
|
+
Optional features can be installed in the same way:
|
|
76
64
|
|
|
77
65
|
```bash
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.venv/bin/python -c "import diskmelts; print(diskmelts.__name__)"
|
|
66
|
+
python -m pip install "diskmelts[io]" # FITS input
|
|
67
|
+
python -m pip install "diskmelts[io,notebooks]" # FITS input and JupyterLab
|
|
81
68
|
```
|
|
82
69
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### Conda or Mamba
|
|
89
|
-
|
|
90
|
-
The YAML files use Conda only for Python and pip; pip installs the package and
|
|
91
|
-
its declared dependencies. Choose one from the repository root:
|
|
92
|
-
|
|
93
|
-
| File | Contents |
|
|
94
|
-
| --- | --- |
|
|
95
|
-
| `environment.yaml` | Core package |
|
|
96
|
-
| `environment-notebooks.yaml` | Core package, FITS support, JupyterLab |
|
|
97
|
-
| `environment-dev.yaml` | Notebook environment, tests, documentation tools |
|
|
98
|
-
|
|
99
|
-
For the notebook setup, run `mamba env create --file environment-notebooks.yaml`
|
|
100
|
-
then `conda activate diskmelts-notebooks`. `conda env create --file` works with
|
|
101
|
-
the same files when using a current Conda release. On Apple Silicon, use a
|
|
102
|
-
native `osx-arm64` installation such as Miniforge; an older Intel Conda can
|
|
103
|
-
still spend a long time solving even these smaller files.
|
|
70
|
+
The pip package includes the production checkpoints and continuum reference
|
|
71
|
+
tables. Full slab grids for training and direct-grid fitting are separate
|
|
72
|
+
downloads; pass their local path as `grid_root`.
|
|
73
|
+
Example notebooks and spectra can be downloaded from the repository when
|
|
74
|
+
needed; they are not installed by pip.
|
|
104
75
|
|
|
105
76
|
## Runtime dependencies
|
|
106
77
|
|
|
@@ -158,34 +129,44 @@ special regions, FITS input, saving CSVs, and uncertainty diagnostics.
|
|
|
158
129
|
|
|
159
130
|
## Quick fitting example
|
|
160
131
|
|
|
132
|
+
Use a continuum-subtracted CSV with a header and `wave,flux` as its first two
|
|
133
|
+
columns, with wavelength in µm and flux in Jy. For a runnable example without
|
|
134
|
+
cloning the repository, download the
|
|
135
|
+
[J16120505 spectrum](https://github.com/wddlx/DiskMELTS/blob/main/Realobs_data/Consub_data/j16120505_v9.0_contsub_RVcorr.csv)
|
|
136
|
+
as `j16120505_contsub.csv`, or run:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
curl -L -o j16120505_contsub.csv https://raw.githubusercontent.com/wddlx/DiskMELTS/main/Realobs_data/Consub_data/j16120505_v9.0_contsub_RVcorr.csv
|
|
140
|
+
```
|
|
141
|
+
|
|
161
142
|
```python
|
|
162
|
-
from diskmelts import load_fitting_models, load_observed_spectrum
|
|
143
|
+
from diskmelts import fit_stage, load_fitting_models, load_observed_spectrum
|
|
163
144
|
|
|
164
|
-
|
|
145
|
+
models = load_fitting_models('C2H2')
|
|
165
146
|
|
|
166
147
|
obs_wav, obs_flux = load_observed_spectrum(
|
|
167
|
-
'
|
|
148
|
+
'j16120505_contsub.csv'
|
|
168
149
|
)
|
|
169
150
|
|
|
170
151
|
fit = fit_stage(
|
|
171
152
|
obs_wav,
|
|
172
153
|
obs_flux,
|
|
173
|
-
molecule='
|
|
174
|
-
wavelength_range=(
|
|
175
|
-
pretrained=
|
|
176
|
-
fit_ranges=
|
|
154
|
+
molecule='C2H2',
|
|
155
|
+
wavelength_range=(13.0, 15.0),
|
|
156
|
+
pretrained=models,
|
|
157
|
+
fit_ranges=(13.5, 14.1),
|
|
177
158
|
distance_pc=122.5,
|
|
178
|
-
n_samples=
|
|
179
|
-
n_refine=
|
|
180
|
-
n_top=
|
|
181
|
-
sigma=0.001,
|
|
159
|
+
n_samples=1024,
|
|
160
|
+
n_refine=4,
|
|
161
|
+
n_top=4,
|
|
182
162
|
)
|
|
183
163
|
|
|
184
|
-
print(fit['params'])
|
|
164
|
+
print(fit['params']['C2H2'])
|
|
185
165
|
```
|
|
186
166
|
|
|
187
167
|
You can also read spectra with your own code. DiskMELTS only requires
|
|
188
|
-
one-dimensional `obs_wav` and `obs_flux` arrays.
|
|
168
|
+
one-dimensional `obs_wav` and `obs_flux` arrays. Replace the sample CSV with
|
|
169
|
+
your own file to fit your source; update `distance_pc` and fit intervals.
|
|
189
170
|
|
|
190
171
|
For a complete staged fit-and-subtract workflow from a checkout, edit and run:
|
|
191
172
|
|
|
@@ -114,3 +114,42 @@ def test_grid_stage_rejects_unapplied_isotope_constraints(tmp_path):
|
|
|
114
114
|
output_dir=tmp_path / 'results', detection_screening=False,
|
|
115
115
|
)
|
|
116
116
|
assert not (tmp_path / 'results').exists()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_repeated_molecule_has_independent_component_bounds(tmp_path):
|
|
120
|
+
wav, spectrum = _small_grid(tmp_path)
|
|
121
|
+
observed = 0.7 * spectrum(110, 13.04) + 1.4 * spectrum(140, 13.20)
|
|
122
|
+
stage = {
|
|
123
|
+
'name': 'acetylene', 'mol': ['C2H2', 'C2H2'],
|
|
124
|
+
'component_names': ['C2H2_thin', 'C2H2_thick'],
|
|
125
|
+
'wavelength_range': (11, 13), 'fit_ranges': (11, 13),
|
|
126
|
+
'T_bounds': (100, 150),
|
|
127
|
+
'logN_bounds': {'C2H2_thin': (13.0, 13.1),
|
|
128
|
+
'C2H2_thick': (13.15, 13.25)},
|
|
129
|
+
'loga_bounds': {'C2H2_thin': (-1.0, 0.0),
|
|
130
|
+
'C2H2_thick': (0.0, 1.0)},
|
|
131
|
+
}
|
|
132
|
+
input_path = tmp_path / 'observation.csv'
|
|
133
|
+
pd.DataFrame({'wave': wav, 'flux': observed}).to_csv(input_path, index=False)
|
|
134
|
+
result = fit_observation_grids(
|
|
135
|
+
input_path, [stage], grid_root=tmp_path / 'grids',
|
|
136
|
+
output_dir=tmp_path / 'results', sigma_noise=1e-4,
|
|
137
|
+
detection_screening=False, maxiter=12, popsize=5, n_refine=2,
|
|
138
|
+
)
|
|
139
|
+
params = result['fits']['acetylene']['params']
|
|
140
|
+
assert set(params) == {'C2H2_thin', 'C2H2_thick'}
|
|
141
|
+
assert 13.0 <= params['C2H2_thin']['logN'] <= 13.1
|
|
142
|
+
assert 13.15 <= params['C2H2_thick']['logN'] <= 13.25
|
|
143
|
+
assert 0.1 <= params['C2H2_thin']['A'] <= 1.0
|
|
144
|
+
assert 1.0 <= params['C2H2_thick']['A'] <= 10.0
|
|
145
|
+
spectrum_csv = pd.read_csv(result['products']['acetylene']['spectrum'])
|
|
146
|
+
assert {'C2H2_thin_model', 'C2H2_thick_model'} <= set(spectrum_csv.columns)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_repeated_grid_molecule_requires_unique_labels(tmp_path):
|
|
150
|
+
wav, spectrum = _small_grid(tmp_path)
|
|
151
|
+
with pytest.raises(ValueError, match='component labels must be unique'):
|
|
152
|
+
fit_grid_stage(
|
|
153
|
+
wav, spectrum(125, 13.125), ['C2H2', 'C2H2'],
|
|
154
|
+
(11, 13), (11, 13), grid_root=tmp_path / 'grids',
|
|
155
|
+
)
|
|
@@ -8,6 +8,44 @@ pytest.importorskip('torch')
|
|
|
8
8
|
from diskmelts import fitting
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
def test_repeated_model_molecule_uses_component_bounds(monkeypatch):
|
|
12
|
+
wave = np.linspace(13.0, 14.0, 41)
|
|
13
|
+
bases = (np.exp(-((wave - 13.3) / 0.1) ** 2),
|
|
14
|
+
np.exp(-((wave - 13.7) / 0.1) ** 2))
|
|
15
|
+
|
|
16
|
+
def fake_flux(model, T, logN, A, obs_wav):
|
|
17
|
+
shape = bases[0] + (logN - 14.0) * bases[1]
|
|
18
|
+
return A * np.interp(obs_wav, wave, shape)
|
|
19
|
+
|
|
20
|
+
monkeypatch.setattr(fitting, '_mol_flux_on_wav', fake_flux)
|
|
21
|
+
observed = fake_flux(None, 300, 14.5, 0.8, wave) + fake_flux(None, 500, 16.5, 1.5, wave)
|
|
22
|
+
fit = fitting.fit_molecules(
|
|
23
|
+
wave, observed, ['C2H2', 'C2H2'], {'C2H2': {'mol': 'C2H2'}},
|
|
24
|
+
(13.0, 14.0), component_names=['C2H2_thin', 'C2H2_thick'],
|
|
25
|
+
T_bounds=(200, 600),
|
|
26
|
+
logN_bounds={'C2H2_thin': (14.0, 15.0),
|
|
27
|
+
'C2H2_thick': (16.0, 17.0)},
|
|
28
|
+
loga_bounds={'C2H2_thin': (-1.0, 0.0),
|
|
29
|
+
'C2H2_thick': (0.0, 1.0)},
|
|
30
|
+
n_samples=16, n_refine=2, n_top=2, seed=7, verbose=False,
|
|
31
|
+
)
|
|
32
|
+
params = fit['params']
|
|
33
|
+
assert set(params) == {'C2H2_thin', 'C2H2_thick'}
|
|
34
|
+
assert 14.0 <= params['C2H2_thin']['logN'] <= 15.0
|
|
35
|
+
assert 16.0 <= params['C2H2_thick']['logN'] <= 17.0
|
|
36
|
+
assert 0.1 <= params['C2H2_thin']['A'] <= 1.0
|
|
37
|
+
assert 1.0 <= params['C2H2_thick']['A'] <= 10.0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_repeated_model_molecule_requires_unique_labels():
|
|
41
|
+
with pytest.raises(ValueError, match='component labels must be unique'):
|
|
42
|
+
fitting.fit_molecules(
|
|
43
|
+
np.array([13.0, 14.0]), np.array([1.0, 1.0]),
|
|
44
|
+
['C2H2', 'C2H2'], {'C2H2': {}}, (13.0, 14.0),
|
|
45
|
+
verbose=False,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
11
49
|
def test_shared_area_groups_use_one_amplitude_in_joint_fit(monkeypatch):
|
|
12
50
|
wave = np.linspace(13.0, 16.0, 80)
|
|
13
51
|
profiles = {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|