fips 0.1.0b3__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.
- fips-0.1.0b3/LICENSE +21 -0
- fips-0.1.0b3/PKG-INFO +185 -0
- fips-0.1.0b3/README.md +152 -0
- fips-0.1.0b3/pyproject.toml +130 -0
- fips-0.1.0b3/setup.cfg +4 -0
- fips-0.1.0b3/src/fips/__init__.py +35 -0
- fips-0.1.0b3/src/fips/aggregators.py +377 -0
- fips-0.1.0b3/src/fips/base.py +608 -0
- fips-0.1.0b3/src/fips/covariance.py +344 -0
- fips-0.1.0b3/src/fips/estimators.py +648 -0
- fips-0.1.0b3/src/fips/filters.py +95 -0
- fips-0.1.0b3/src/fips/indexes.py +222 -0
- fips-0.1.0b3/src/fips/kernels.py +113 -0
- fips-0.1.0b3/src/fips/matrix.py +358 -0
- fips-0.1.0b3/src/fips/metrics.py +97 -0
- fips-0.1.0b3/src/fips/operators.py +96 -0
- fips-0.1.0b3/src/fips/pipeline.py +225 -0
- fips-0.1.0b3/src/fips/problem.py +359 -0
- fips-0.1.0b3/src/fips/problems/__init__.py +6 -0
- fips-0.1.0b3/src/fips/problems/flux/__init__.py +25 -0
- fips-0.1.0b3/src/fips/problems/flux/pipeline.py +229 -0
- fips-0.1.0b3/src/fips/problems/flux/problem.py +129 -0
- fips-0.1.0b3/src/fips/problems/flux/transport/__init__.py +7 -0
- fips-0.1.0b3/src/fips/problems/flux/transport/stilt/__init__.py +19 -0
- fips-0.1.0b3/src/fips/problems/flux/transport/stilt/builder.py +235 -0
- fips-0.1.0b3/src/fips/problems/flux/transport/stilt/errors.py +157 -0
- fips-0.1.0b3/src/fips/problems/flux/visualization.py +458 -0
- fips-0.1.0b3/src/fips/py.typed +2 -0
- fips-0.1.0b3/src/fips/vector.py +250 -0
- fips-0.1.0b3/src/fips/visualization.py +249 -0
- fips-0.1.0b3/src/fips.egg-info/PKG-INFO +185 -0
- fips-0.1.0b3/src/fips.egg-info/SOURCES.txt +50 -0
- fips-0.1.0b3/src/fips.egg-info/dependency_links.txt +1 -0
- fips-0.1.0b3/src/fips.egg-info/requires.txt +12 -0
- fips-0.1.0b3/src/fips.egg-info/top_level.txt +1 -0
- fips-0.1.0b3/tests/test_aggregators.py +381 -0
- fips-0.1.0b3/tests/test_base.py +228 -0
- fips-0.1.0b3/tests/test_covariance.py +562 -0
- fips-0.1.0b3/tests/test_estimators.py +867 -0
- fips-0.1.0b3/tests/test_filters.py +44 -0
- fips-0.1.0b3/tests/test_flux_problem.py +322 -0
- fips-0.1.0b3/tests/test_indexes.py +151 -0
- fips-0.1.0b3/tests/test_kernels.py +42 -0
- fips-0.1.0b3/tests/test_matrices.py +515 -0
- fips-0.1.0b3/tests/test_metrics.py +79 -0
- fips-0.1.0b3/tests/test_operators.py +360 -0
- fips-0.1.0b3/tests/test_pkg.py +1 -0
- fips-0.1.0b3/tests/test_problem.py +593 -0
- fips-0.1.0b3/tests/test_sparse.py +344 -0
- fips-0.1.0b3/tests/test_stilt_builder.py +209 -0
- fips-0.1.0b3/tests/test_vectors.py +285 -0
- fips-0.1.0b3/tests/test_visualization.py +264 -0
fips-0.1.0b3/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 James Mineau
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fips-0.1.0b3/PKG-INFO
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fips
|
|
3
|
+
Version: 0.1.0b3
|
|
4
|
+
Summary: Flexible Inverse Problem Solver (FIPS)
|
|
5
|
+
Author-email: James Mineau <jameskmineau@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jmineau/fips
|
|
8
|
+
Project-URL: Documentation, https://jmineau.github.io/fips/
|
|
9
|
+
Project-URL: Repository, https://github.com/jmineau/fips
|
|
10
|
+
Project-URL: Issues, https://github.com/jmineau/fips/issues
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: joblib>=1.4.0
|
|
22
|
+
Requires-Dist: numpy>=1.22.0
|
|
23
|
+
Requires-Dist: pandas>=2.0.0
|
|
24
|
+
Requires-Dist: scipy>=1.8.0
|
|
25
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
26
|
+
Requires-Dist: xarray>=2022.3.0
|
|
27
|
+
Provides-Extra: flux
|
|
28
|
+
Requires-Dist: cartopy>=0.24.0; extra == "flux"
|
|
29
|
+
Requires-Dist: h5py>=3.11.0; extra == "flux"
|
|
30
|
+
Requires-Dist: matplotlib>=3.6.0; extra == "flux"
|
|
31
|
+
Requires-Dist: pystilt>=0.1.0a1; extra == "flux"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
<div align=center>
|
|
35
|
+
<picture>
|
|
36
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jmineau/fips/main/docs/_static/logo_dark.png">
|
|
37
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/jmineau/fips/main/docs/_static/logo.png">
|
|
38
|
+
<img alt="fips logo" src="https://raw.githubusercontent.com/jmineau/fips/main/docs/_static/logo.png" width="600">
|
|
39
|
+
</picture>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
[](https://github.com/jmineau/fips/actions/workflows/tests.yml)
|
|
43
|
+
[](https://github.com/jmineau/fips/actions/workflows/docs.yml)
|
|
44
|
+
[](https://github.com/jmineau/fips/actions/workflows/quality.yml)
|
|
45
|
+
[](https://codecov.io/gh/jmineau/fips)
|
|
46
|
+
[](https://opensource.org/licenses/MIT)
|
|
47
|
+
[](https://github.com/astral-sh/ruff)
|
|
48
|
+
[](https://github.com/microsoft/pyright)
|
|
49
|
+
|
|
50
|
+
> **⚠️ Beta Release**: FIPS is currently in beta (v0.1.0b1). The core API is stable and ready for testing, but may evolve based on user feedback. Please [report any issues or edge cases](https://github.com/jmineau/fips/issues) you encounter.
|
|
51
|
+
|
|
52
|
+
## Bridging Physics and Data
|
|
53
|
+
Inverse problems in geophysics and atmospheric science are incredibly complex, often involving massive state spaces, deeply heterogeneous observational networks, and explicit matrix-algebra requirements. While many general-purpose optimization tools exist, they often force researchers to strip away critical spatiotemporal metadata or translate pre-computed physical models into rigid, abstract array structures.
|
|
54
|
+
|
|
55
|
+
**FIPS (Flexible Inverse Problem Solver)** is built from the ground up to solve linear, matrix-based inverse problems without losing the context of your data. It provides the structural flexibility to handle messy, real-world realities seamlessly:
|
|
56
|
+
|
|
57
|
+
- **Native N-Dimensional Alignment**: FIPS natively utilizes `pandas.MultiIndex` to smoothly align heterogeneous datasets across any dimension. Whether you are mixing temporal, spatial, spectral, or sensor-specific data, your coordinates are never dropped or misaligned.
|
|
58
|
+
|
|
59
|
+
- **Modular Block Architecture**: Avoid wrangling monolithic arrays. Construct massive, multi-source state spaces and observation networks piece-by-piece using specialized `Block` and `MatrixBlock` objects.
|
|
60
|
+
|
|
61
|
+
- **Speak Your Domain's Language**: Built explicitly around the standard `y = Hx + error` paradigm. Directly plug in your pre-computed forward operators ($H$), prior covariances ($S_0$), and model-data mismatches ($S_z$).
|
|
62
|
+
|
|
63
|
+
- **Analytical Speed & Sparse Support**: FIPS is built for scale. By leveraging optimized sparse data structures and direct linear algebra rather than expensive sampling algorithms, it computes exact analytical Maximum A Posteriori (MAP) estimates for massive state spaces in seconds.
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
### From GitHub
|
|
68
|
+
```bash
|
|
69
|
+
pip install git+https://github.com/jmineau/fips
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### From Source
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/jmineau/fips.git
|
|
76
|
+
cd fips
|
|
77
|
+
pip install -e .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
### Single-block — one observation source
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
import numpy as np
|
|
86
|
+
import pandas as pd
|
|
87
|
+
from fips import Block, CovarianceMatrix
|
|
88
|
+
from fips.problems.flux import FluxProblem
|
|
89
|
+
|
|
90
|
+
# State: gridded prior fluxes (time × lat × lon)
|
|
91
|
+
flux_idx = pd.MultiIndex.from_product(
|
|
92
|
+
[pd.date_range("2023-01", periods=3, freq="MS"), [37.0, 38.0], [-112.0, -111.0]],
|
|
93
|
+
names=["time", "lat", "lon"],
|
|
94
|
+
)
|
|
95
|
+
prior = pd.Series(np.ones(12) * 1.5, index=flux_idx, name="flux")
|
|
96
|
+
|
|
97
|
+
# Observations: tower concentration measurements
|
|
98
|
+
obs_idx = pd.MultiIndex.from_product(
|
|
99
|
+
[pd.date_range("2023-01", periods=8, freq="2W"), ["UOU"]],
|
|
100
|
+
names=["time", "site"],
|
|
101
|
+
)
|
|
102
|
+
obs = pd.Series(np.ones(8) * 400.0, index=obs_idx, name="concentration")
|
|
103
|
+
|
|
104
|
+
# Forward operator (Jacobian), flux error covariance, obs error covariance
|
|
105
|
+
H = pd.DataFrame(np.random.rand(8, 12), index=obs_idx, columns=flux_idx)
|
|
106
|
+
S_0 = pd.DataFrame(np.eye(12) * 0.5, index=flux_idx, columns=flux_idx)
|
|
107
|
+
S_z = pd.DataFrame(np.eye(8) * 0.1, index=obs_idx, columns=obs_idx)
|
|
108
|
+
|
|
109
|
+
problem = FluxProblem(
|
|
110
|
+
obs=obs, prior=prior,
|
|
111
|
+
forward_operator=H, prior_error=S_0, modeldata_mismatch=S_z,
|
|
112
|
+
).solve()
|
|
113
|
+
|
|
114
|
+
print(problem.posterior_fluxes) # posterior pd.Series indexed by (time, lat, lon)
|
|
115
|
+
print(problem.estimator.reduced_chi2) # reduced chi-squared statistic
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Multi-block — combined station + satellite observations
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
import numpy as np
|
|
122
|
+
import pandas as pd
|
|
123
|
+
from fips import Block, Vector, Matrix, MatrixBlock, CovarianceMatrix, InverseProblem
|
|
124
|
+
|
|
125
|
+
# State: same gridded prior fluxes (from above)
|
|
126
|
+
N_f = 12
|
|
127
|
+
|
|
128
|
+
# Obs block 1: ground station in-situ concentrations
|
|
129
|
+
station_idx = pd.MultiIndex.from_product(
|
|
130
|
+
[pd.date_range("2023-01", periods=8, freq="2W"), ["UOU"]],
|
|
131
|
+
names=["time", "site"],
|
|
132
|
+
)
|
|
133
|
+
station_obs = Block(pd.Series(np.ones(8) * 400.0,
|
|
134
|
+
index=station_idx, name="station"))
|
|
135
|
+
|
|
136
|
+
# Obs block 2: satellite column-average concentrations
|
|
137
|
+
satellite_idx = pd.MultiIndex.from_product(
|
|
138
|
+
[pd.date_range("2023-01", periods=3, freq="MS"), [37.5], [-111.5]],
|
|
139
|
+
names=["time", "lat", "lon"],
|
|
140
|
+
)
|
|
141
|
+
satellite_obs = Block(pd.Series(np.ones(3) * 0.00400,
|
|
142
|
+
index=satellite_idx, name="satellite"))
|
|
143
|
+
|
|
144
|
+
# Combine obs blocks into a list
|
|
145
|
+
obs_blks = [station_obs, satellite_obs]
|
|
146
|
+
|
|
147
|
+
# Jacobian: one MatrixBlock per obs type, both mapping to the "flux" state block
|
|
148
|
+
H_blks = [
|
|
149
|
+
MatrixBlock(
|
|
150
|
+
pd.DataFrame(np.random.rand(8, N_f),
|
|
151
|
+
index=station_idx, columns=flux_idx),
|
|
152
|
+
row_block="station", col_block="flux",
|
|
153
|
+
),
|
|
154
|
+
MatrixBlock(
|
|
155
|
+
pd.DataFrame(np.random.rand(3, N_f),
|
|
156
|
+
index=satellite_idx, columns=flux_idx),
|
|
157
|
+
row_block="satellite", col_block="flux",
|
|
158
|
+
),]
|
|
159
|
+
|
|
160
|
+
# Prior error covariance: only flux errors, no cross-block covariances
|
|
161
|
+
S_0 = CovarianceMatrix(np.eye(N_f) * 0.5, index=flux_idx, columns=flux_idx)
|
|
162
|
+
|
|
163
|
+
# Model-data mismatch covariance: block-diagonal with separate error levels for stations vs. satellite
|
|
164
|
+
S_z_blks = [
|
|
165
|
+
CovarianceMatrix(np.eye(8) * 0.1, index=station_idx, columns=station_idx),
|
|
166
|
+
CovarianceMatrix(np.eye(3) * 0.2, index=satellite_idx, columns=satellite_idx),
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
# Pass blocks to the InverseProblem and solve
|
|
170
|
+
problem = InverseProblem(
|
|
171
|
+
obs=obs_blks, prior=prior,
|
|
172
|
+
forward_operator=H_blks, prior_error=S_0, modeldata_mismatch=S_z_blks,
|
|
173
|
+
).solve()
|
|
174
|
+
|
|
175
|
+
print(problem.posterior['flux']) # posterior pd.Series indexed by (time, lat, lon)
|
|
176
|
+
print(problem.estimator.reduced_chi2) # reduced chi-squared statistic
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Documentation
|
|
180
|
+
|
|
181
|
+
Full documentation is available at [https://jmineau.github.io/fips/](https://jmineau.github.io/fips/)
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
fips-0.1.0b3/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<div align=center>
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jmineau/fips/main/docs/_static/logo_dark.png">
|
|
4
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/jmineau/fips/main/docs/_static/logo.png">
|
|
5
|
+
<img alt="fips logo" src="https://raw.githubusercontent.com/jmineau/fips/main/docs/_static/logo.png" width="600">
|
|
6
|
+
</picture>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
[](https://github.com/jmineau/fips/actions/workflows/tests.yml)
|
|
10
|
+
[](https://github.com/jmineau/fips/actions/workflows/docs.yml)
|
|
11
|
+
[](https://github.com/jmineau/fips/actions/workflows/quality.yml)
|
|
12
|
+
[](https://codecov.io/gh/jmineau/fips)
|
|
13
|
+
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
[](https://github.com/astral-sh/ruff)
|
|
15
|
+
[](https://github.com/microsoft/pyright)
|
|
16
|
+
|
|
17
|
+
> **⚠️ Beta Release**: FIPS is currently in beta (v0.1.0b1). The core API is stable and ready for testing, but may evolve based on user feedback. Please [report any issues or edge cases](https://github.com/jmineau/fips/issues) you encounter.
|
|
18
|
+
|
|
19
|
+
## Bridging Physics and Data
|
|
20
|
+
Inverse problems in geophysics and atmospheric science are incredibly complex, often involving massive state spaces, deeply heterogeneous observational networks, and explicit matrix-algebra requirements. While many general-purpose optimization tools exist, they often force researchers to strip away critical spatiotemporal metadata or translate pre-computed physical models into rigid, abstract array structures.
|
|
21
|
+
|
|
22
|
+
**FIPS (Flexible Inverse Problem Solver)** is built from the ground up to solve linear, matrix-based inverse problems without losing the context of your data. It provides the structural flexibility to handle messy, real-world realities seamlessly:
|
|
23
|
+
|
|
24
|
+
- **Native N-Dimensional Alignment**: FIPS natively utilizes `pandas.MultiIndex` to smoothly align heterogeneous datasets across any dimension. Whether you are mixing temporal, spatial, spectral, or sensor-specific data, your coordinates are never dropped or misaligned.
|
|
25
|
+
|
|
26
|
+
- **Modular Block Architecture**: Avoid wrangling monolithic arrays. Construct massive, multi-source state spaces and observation networks piece-by-piece using specialized `Block` and `MatrixBlock` objects.
|
|
27
|
+
|
|
28
|
+
- **Speak Your Domain's Language**: Built explicitly around the standard `y = Hx + error` paradigm. Directly plug in your pre-computed forward operators ($H$), prior covariances ($S_0$), and model-data mismatches ($S_z$).
|
|
29
|
+
|
|
30
|
+
- **Analytical Speed & Sparse Support**: FIPS is built for scale. By leveraging optimized sparse data structures and direct linear algebra rather than expensive sampling algorithms, it computes exact analytical Maximum A Posteriori (MAP) estimates for massive state spaces in seconds.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
### From GitHub
|
|
35
|
+
```bash
|
|
36
|
+
pip install git+https://github.com/jmineau/fips
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### From Source
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/jmineau/fips.git
|
|
43
|
+
cd fips
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
### Single-block — one observation source
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import numpy as np
|
|
53
|
+
import pandas as pd
|
|
54
|
+
from fips import Block, CovarianceMatrix
|
|
55
|
+
from fips.problems.flux import FluxProblem
|
|
56
|
+
|
|
57
|
+
# State: gridded prior fluxes (time × lat × lon)
|
|
58
|
+
flux_idx = pd.MultiIndex.from_product(
|
|
59
|
+
[pd.date_range("2023-01", periods=3, freq="MS"), [37.0, 38.0], [-112.0, -111.0]],
|
|
60
|
+
names=["time", "lat", "lon"],
|
|
61
|
+
)
|
|
62
|
+
prior = pd.Series(np.ones(12) * 1.5, index=flux_idx, name="flux")
|
|
63
|
+
|
|
64
|
+
# Observations: tower concentration measurements
|
|
65
|
+
obs_idx = pd.MultiIndex.from_product(
|
|
66
|
+
[pd.date_range("2023-01", periods=8, freq="2W"), ["UOU"]],
|
|
67
|
+
names=["time", "site"],
|
|
68
|
+
)
|
|
69
|
+
obs = pd.Series(np.ones(8) * 400.0, index=obs_idx, name="concentration")
|
|
70
|
+
|
|
71
|
+
# Forward operator (Jacobian), flux error covariance, obs error covariance
|
|
72
|
+
H = pd.DataFrame(np.random.rand(8, 12), index=obs_idx, columns=flux_idx)
|
|
73
|
+
S_0 = pd.DataFrame(np.eye(12) * 0.5, index=flux_idx, columns=flux_idx)
|
|
74
|
+
S_z = pd.DataFrame(np.eye(8) * 0.1, index=obs_idx, columns=obs_idx)
|
|
75
|
+
|
|
76
|
+
problem = FluxProblem(
|
|
77
|
+
obs=obs, prior=prior,
|
|
78
|
+
forward_operator=H, prior_error=S_0, modeldata_mismatch=S_z,
|
|
79
|
+
).solve()
|
|
80
|
+
|
|
81
|
+
print(problem.posterior_fluxes) # posterior pd.Series indexed by (time, lat, lon)
|
|
82
|
+
print(problem.estimator.reduced_chi2) # reduced chi-squared statistic
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Multi-block — combined station + satellite observations
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
import numpy as np
|
|
89
|
+
import pandas as pd
|
|
90
|
+
from fips import Block, Vector, Matrix, MatrixBlock, CovarianceMatrix, InverseProblem
|
|
91
|
+
|
|
92
|
+
# State: same gridded prior fluxes (from above)
|
|
93
|
+
N_f = 12
|
|
94
|
+
|
|
95
|
+
# Obs block 1: ground station in-situ concentrations
|
|
96
|
+
station_idx = pd.MultiIndex.from_product(
|
|
97
|
+
[pd.date_range("2023-01", periods=8, freq="2W"), ["UOU"]],
|
|
98
|
+
names=["time", "site"],
|
|
99
|
+
)
|
|
100
|
+
station_obs = Block(pd.Series(np.ones(8) * 400.0,
|
|
101
|
+
index=station_idx, name="station"))
|
|
102
|
+
|
|
103
|
+
# Obs block 2: satellite column-average concentrations
|
|
104
|
+
satellite_idx = pd.MultiIndex.from_product(
|
|
105
|
+
[pd.date_range("2023-01", periods=3, freq="MS"), [37.5], [-111.5]],
|
|
106
|
+
names=["time", "lat", "lon"],
|
|
107
|
+
)
|
|
108
|
+
satellite_obs = Block(pd.Series(np.ones(3) * 0.00400,
|
|
109
|
+
index=satellite_idx, name="satellite"))
|
|
110
|
+
|
|
111
|
+
# Combine obs blocks into a list
|
|
112
|
+
obs_blks = [station_obs, satellite_obs]
|
|
113
|
+
|
|
114
|
+
# Jacobian: one MatrixBlock per obs type, both mapping to the "flux" state block
|
|
115
|
+
H_blks = [
|
|
116
|
+
MatrixBlock(
|
|
117
|
+
pd.DataFrame(np.random.rand(8, N_f),
|
|
118
|
+
index=station_idx, columns=flux_idx),
|
|
119
|
+
row_block="station", col_block="flux",
|
|
120
|
+
),
|
|
121
|
+
MatrixBlock(
|
|
122
|
+
pd.DataFrame(np.random.rand(3, N_f),
|
|
123
|
+
index=satellite_idx, columns=flux_idx),
|
|
124
|
+
row_block="satellite", col_block="flux",
|
|
125
|
+
),]
|
|
126
|
+
|
|
127
|
+
# Prior error covariance: only flux errors, no cross-block covariances
|
|
128
|
+
S_0 = CovarianceMatrix(np.eye(N_f) * 0.5, index=flux_idx, columns=flux_idx)
|
|
129
|
+
|
|
130
|
+
# Model-data mismatch covariance: block-diagonal with separate error levels for stations vs. satellite
|
|
131
|
+
S_z_blks = [
|
|
132
|
+
CovarianceMatrix(np.eye(8) * 0.1, index=station_idx, columns=station_idx),
|
|
133
|
+
CovarianceMatrix(np.eye(3) * 0.2, index=satellite_idx, columns=satellite_idx),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
# Pass blocks to the InverseProblem and solve
|
|
137
|
+
problem = InverseProblem(
|
|
138
|
+
obs=obs_blks, prior=prior,
|
|
139
|
+
forward_operator=H_blks, prior_error=S_0, modeldata_mismatch=S_z_blks,
|
|
140
|
+
).solve()
|
|
141
|
+
|
|
142
|
+
print(problem.posterior['flux']) # posterior pd.Series indexed by (time, lat, lon)
|
|
143
|
+
print(problem.estimator.reduced_chi2) # reduced chi-squared statistic
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Documentation
|
|
147
|
+
|
|
148
|
+
Full documentation is available at [https://jmineau.github.io/fips/](https://jmineau.github.io/fips/)
|
|
149
|
+
|
|
150
|
+
## Contributing
|
|
151
|
+
|
|
152
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fips"
|
|
7
|
+
version = "0.1.0b3"
|
|
8
|
+
description = "Flexible Inverse Problem Solver (FIPS)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "James Mineau", email = "jameskmineau@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"joblib>=1.4.0",
|
|
26
|
+
"numpy>=1.22.0",
|
|
27
|
+
"pandas>=2.0.0", # need pandas 2.0 for upgraded Index object
|
|
28
|
+
"scipy>=1.8.0",
|
|
29
|
+
"typing-extensions>=4.0.0",
|
|
30
|
+
"xarray>=2022.3.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
flux = [
|
|
35
|
+
"cartopy>=0.24.0", # numpy 2.0 support
|
|
36
|
+
"h5py>=3.11.0", # numpy 2.0 support
|
|
37
|
+
"matplotlib>=3.6.0",
|
|
38
|
+
"pystilt>=0.1.0a1",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[dependency-groups]
|
|
42
|
+
dev = [
|
|
43
|
+
"fips[flux]",
|
|
44
|
+
"docstr-coverage>=2.0.0",
|
|
45
|
+
"ipykernel>=7.2.0",
|
|
46
|
+
"myst-parser>=2.0.0",
|
|
47
|
+
"nbsphinx>=0.9.0",
|
|
48
|
+
"pre-commit>=3.0.0",
|
|
49
|
+
"pydata-sphinx-theme>=0.14.0",
|
|
50
|
+
"pyright[nodejs]>=1.1.0",
|
|
51
|
+
"pytest>=7.0.0",
|
|
52
|
+
"pytest-cov>=4.0.0",
|
|
53
|
+
"ruff>=0.1.0",
|
|
54
|
+
"sphinx>=7.0.0",
|
|
55
|
+
"sphinx-autodoc-typehints>=1.24.0",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[project.urls]
|
|
59
|
+
Homepage = "https://github.com/jmineau/fips"
|
|
60
|
+
Documentation = "https://jmineau.github.io/fips/"
|
|
61
|
+
Repository = "https://github.com/jmineau/fips"
|
|
62
|
+
Issues = "https://github.com/jmineau/fips/issues"
|
|
63
|
+
|
|
64
|
+
[tool.coverage.run]
|
|
65
|
+
source = ["src/fips"]
|
|
66
|
+
omit = ["*/tests/*", "*/test_*.py"]
|
|
67
|
+
|
|
68
|
+
[tool.coverage.report]
|
|
69
|
+
# Regexes for lines to exclude from consideration
|
|
70
|
+
exclude_also = [
|
|
71
|
+
# Don't complain about missing debug-only code:
|
|
72
|
+
"def __repr__",
|
|
73
|
+
"if self\\.debug",
|
|
74
|
+
|
|
75
|
+
# Don't complain if tests don't hit defensive assertion code:
|
|
76
|
+
"raise AssertionError",
|
|
77
|
+
"raise NotImplementedError",
|
|
78
|
+
|
|
79
|
+
# Don't complain if non-runnable code isn't run:
|
|
80
|
+
"if 0:",
|
|
81
|
+
"if __name__ == .__main__.:",
|
|
82
|
+
|
|
83
|
+
# Don't complain about abstract methods, they aren't run:
|
|
84
|
+
"@(abc\\.)?abstractmethod",
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
[tool.pyright]
|
|
88
|
+
include = ["src"]
|
|
89
|
+
exclude = ["**/__pycache__", "**/.venv"]
|
|
90
|
+
|
|
91
|
+
[tool.pytest.ini_options]
|
|
92
|
+
testpaths = ["tests"]
|
|
93
|
+
filterwarnings = [
|
|
94
|
+
"ignore:numpy.core.multiarray is deprecated:DeprecationWarning",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[tool.ruff]
|
|
98
|
+
target-version = "py310"
|
|
99
|
+
|
|
100
|
+
[tool.ruff.lint.pydocstyle]
|
|
101
|
+
convention = "numpy"
|
|
102
|
+
|
|
103
|
+
[tool.ruff.format]
|
|
104
|
+
docstring-code-format = true
|
|
105
|
+
|
|
106
|
+
[tool.ruff.lint]
|
|
107
|
+
select = [
|
|
108
|
+
"E", # pycodestyle
|
|
109
|
+
"F", # Pyflakes
|
|
110
|
+
"UP", # pyupgrade
|
|
111
|
+
"B", # flake8-bugbear
|
|
112
|
+
"SIM", # flake8-simplify
|
|
113
|
+
"I", # isort
|
|
114
|
+
"D", # pydocstyle
|
|
115
|
+
]
|
|
116
|
+
ignore = [
|
|
117
|
+
"E501", # line too long, handled by formatter
|
|
118
|
+
"D200", # One-line docstring should fit on one line with quotes
|
|
119
|
+
"D212", # Multi-line docstring summary should start at the first line
|
|
120
|
+
"D400", # First line should end with a period, question mark, or exclamation point
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
[tool.ruff.lint.isort]
|
|
124
|
+
known-first-party = ["fips"]
|
|
125
|
+
|
|
126
|
+
[tool.setuptools.packages.find]
|
|
127
|
+
where = ["src"]
|
|
128
|
+
|
|
129
|
+
[tool.setuptools.package-data]
|
|
130
|
+
"fips" = ["py.typed"]
|
fips-0.1.0b3/setup.cfg
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flexible Inverse Problem Solver (FIPS).
|
|
3
|
+
|
|
4
|
+
A Pythonic framework for solving linear inverse problems using Bayesian estimation.
|
|
5
|
+
Provides data structures for state vectors, observations, forward operators, and
|
|
6
|
+
covariance matrices; estimators for computing posteriors; and interfaces for
|
|
7
|
+
serialization, visualization, and specialized applications like atmospheric
|
|
8
|
+
flux inversion.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
from .covariance import CovarianceMatrix
|
|
14
|
+
from .estimators import Estimator, available_estimators
|
|
15
|
+
from .matrix import Matrix, MatrixBlock
|
|
16
|
+
from .operators import ForwardOperator, convolve
|
|
17
|
+
from .pipeline import InversionPipeline
|
|
18
|
+
from .problem import InverseProblem
|
|
19
|
+
from .vector import Block, Vector
|
|
20
|
+
|
|
21
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Block",
|
|
25
|
+
"Vector",
|
|
26
|
+
"Matrix",
|
|
27
|
+
"MatrixBlock",
|
|
28
|
+
"Estimator",
|
|
29
|
+
"available_estimators",
|
|
30
|
+
"ForwardOperator",
|
|
31
|
+
"CovarianceMatrix",
|
|
32
|
+
"InversionPipeline",
|
|
33
|
+
"InverseProblem",
|
|
34
|
+
"convolve",
|
|
35
|
+
]
|