pastastore 1.11.0__py3-none-any.whl → 1.12.1__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.
- docs/conf.py +10 -97
- pastastore/base.py +171 -37
- pastastore/connectors.py +151 -25
- pastastore/datasets.py +0 -4
- pastastore/store.py +17 -5
- pastastore/styling.py +4 -2
- pastastore/util.py +3 -0
- pastastore/validator.py +53 -3
- pastastore/version.py +2 -2
- {pastastore-1.11.0.dist-info → pastastore-1.12.1.dist-info}/METADATA +4 -4
- pastastore-1.12.1.dist-info/RECORD +31 -0
- {pastastore-1.11.0.dist-info → pastastore-1.12.1.dist-info}/WHEEL +1 -1
- tests/conftest.py +153 -51
- tests/test_003_pastastore.py +3 -1
- tests/test_009_parallel.py +393 -0
- pastastore-1.11.0.dist-info/RECORD +0 -30
- {pastastore-1.11.0.dist-info → pastastore-1.12.1.dist-info}/licenses/LICENSE +0 -0
- {pastastore-1.11.0.dist-info → pastastore-1.12.1.dist-info}/top_level.txt +0 -0
tests/conftest.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# ruff: noqa: D100 D103
|
|
2
2
|
import importlib
|
|
3
|
+
import inspect
|
|
3
4
|
from importlib import metadata
|
|
4
5
|
|
|
5
6
|
import pandas as pd
|
|
@@ -8,50 +9,94 @@ import pytest
|
|
|
8
9
|
|
|
9
10
|
import pastastore as pst
|
|
10
11
|
|
|
11
|
-
params = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
params = [
|
|
13
|
+
"dict",
|
|
14
|
+
"pas",
|
|
15
|
+
"arcticdb",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture(scope="module")
|
|
20
|
+
def data1():
|
|
21
|
+
d = {
|
|
22
|
+
"oseries1": pd.read_csv("./tests/data/obs.csv", index_col=0, parse_dates=True),
|
|
23
|
+
"oseries1_meta": {"x": 165000, "y": 424000},
|
|
24
|
+
"prec1": pd.read_csv("./tests/data/rain.csv", index_col=0, parse_dates=True),
|
|
25
|
+
"prec1_meta": {"x": 165050, "y": 424050},
|
|
26
|
+
"evap1": pd.read_csv("./tests/data/evap.csv", index_col=0, parse_dates=True),
|
|
27
|
+
"evap1_meta": {"x": 164500, "y": 424000},
|
|
28
|
+
}
|
|
29
|
+
return d
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.fixture(scope="module")
|
|
33
|
+
def data2():
|
|
34
|
+
d = {
|
|
35
|
+
"oseries2": pd.read_csv(
|
|
36
|
+
"./tests/data/head_nb1.csv", index_col=0, parse_dates=True
|
|
37
|
+
),
|
|
38
|
+
"oseries2_meta": {"x": 164000, "y": 423000},
|
|
39
|
+
"prec2": pd.read_csv(
|
|
40
|
+
"./tests/data/rain_nb1.csv", index_col=0, parse_dates=True
|
|
41
|
+
),
|
|
42
|
+
"prec2_meta": {"x": 164010, "y": 423000},
|
|
43
|
+
"evap2": pd.read_csv(
|
|
44
|
+
"./tests/data/evap_nb1.csv", index_col=0, parse_dates=True
|
|
45
|
+
),
|
|
46
|
+
"evap2_meta": {"x": 164000, "y": 423030},
|
|
47
|
+
}
|
|
48
|
+
return d
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.fixture(scope="module")
|
|
52
|
+
def data3():
|
|
53
|
+
w = pd.read_csv("./tests/data/well_month_end.csv", index_col=0, parse_dates=True)
|
|
54
|
+
w = ps.ts.timestep_weighted_resample(
|
|
55
|
+
w,
|
|
56
|
+
pd.date_range(w.index[0] - pd.offsets.MonthBegin(), w.index[-1], freq="D"),
|
|
57
|
+
).bfill()
|
|
24
58
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
59
|
+
d = {
|
|
60
|
+
"oseries3": pd.read_csv(
|
|
61
|
+
"./tests/data/gw_obs.csv", index_col=0, parse_dates=True
|
|
62
|
+
),
|
|
63
|
+
"oseries3_meta": {"x": 165554, "y": 422685},
|
|
64
|
+
"well1": w,
|
|
65
|
+
"well1_meta": {"x": 164691, "y": 423579},
|
|
66
|
+
"well2": w + 10,
|
|
67
|
+
"well2_meta": {"x": 164691 + 2000, "y": 423579 + 2000}, # far away
|
|
68
|
+
}
|
|
69
|
+
return d
|
|
28
70
|
|
|
29
|
-
# prec 1
|
|
30
|
-
s = pd.read_csv("./tests/data/rain.csv", index_col=0, parse_dates=True)
|
|
31
|
-
pstore.add_stress(s, "prec1", kind="prec", metadata={"x": 165050, "y": 424050})
|
|
32
71
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
pstore.add_stress(s, "prec2", kind="prec", metadata={"x": 164010, "y": 423000})
|
|
72
|
+
def initialize_project(conn, data1, data2, data3):
|
|
73
|
+
pstore = pst.PastaStore(conn, "test_project")
|
|
36
74
|
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
pstore.add_stress(
|
|
75
|
+
# dataset 1
|
|
76
|
+
pstore.add_oseries(data1["oseries1"], "oseries1", metadata=data1["oseries1_meta"])
|
|
77
|
+
pstore.add_stress(
|
|
78
|
+
data1["prec1"], "prec1", kind="prec", metadata=data1["prec1_meta"]
|
|
79
|
+
)
|
|
80
|
+
pstore.add_stress(
|
|
81
|
+
data1["evap1"], "evap1", kind="evap", metadata=data1["evap1_meta"]
|
|
82
|
+
)
|
|
40
83
|
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
pstore.add_stress(
|
|
84
|
+
# dataset 2
|
|
85
|
+
pstore.add_oseries(data2["oseries2"], "oseries2", metadata=data2["oseries2_meta"])
|
|
86
|
+
pstore.add_stress(
|
|
87
|
+
data2["prec2"], "prec2", kind="prec", metadata=data2["prec2_meta"]
|
|
88
|
+
)
|
|
89
|
+
pstore.add_stress(
|
|
90
|
+
data2["evap2"], "evap2", kind="evap", metadata=data2["evap2_meta"]
|
|
91
|
+
)
|
|
44
92
|
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
).bfill()
|
|
51
|
-
pstore.add_stress(s, "well1", kind="well", metadata={"x": 164691, "y": 423579})
|
|
52
|
-
# add second well
|
|
93
|
+
# dataset 3
|
|
94
|
+
pstore.add_oseries(data3["oseries3"], "oseries3", metadata=data3["oseries3_meta"])
|
|
95
|
+
pstore.add_stress(
|
|
96
|
+
data3["well1"], "well1", kind="well", metadata=data3["well1_meta"]
|
|
97
|
+
)
|
|
53
98
|
pstore.add_stress(
|
|
54
|
-
|
|
99
|
+
data3["well2"], "well2", kind="well", metadata=data3["well2_meta"]
|
|
55
100
|
)
|
|
56
101
|
|
|
57
102
|
return pstore
|
|
@@ -63,7 +108,7 @@ def conn(request):
|
|
|
63
108
|
name = f"test_{request.param}"
|
|
64
109
|
# connect to dbase
|
|
65
110
|
if request.param == "arcticdb":
|
|
66
|
-
uri = "lmdb://./
|
|
111
|
+
uri = "lmdb://./tests/data/arcticdb/"
|
|
67
112
|
conn = pst.ArcticDBConnector(name, uri)
|
|
68
113
|
elif request.param == "dict":
|
|
69
114
|
conn = pst.DictConnector(name)
|
|
@@ -76,32 +121,25 @@ def conn(request):
|
|
|
76
121
|
|
|
77
122
|
|
|
78
123
|
@pytest.fixture(scope="module", params=params)
|
|
79
|
-
def pstore(request):
|
|
124
|
+
def pstore(request, data1, data2, data3):
|
|
80
125
|
if request.param == "arcticdb":
|
|
81
|
-
name = "
|
|
82
|
-
uri = "lmdb://./
|
|
126
|
+
name = "testdb"
|
|
127
|
+
uri = "lmdb://./tests/data/arcticdb/"
|
|
83
128
|
connector = pst.ArcticDBConnector(name, uri)
|
|
84
129
|
elif request.param == "dict":
|
|
85
|
-
name = "
|
|
130
|
+
name = "testdb"
|
|
86
131
|
connector = pst.DictConnector(name)
|
|
87
132
|
elif request.param == "pas":
|
|
88
|
-
name = "
|
|
133
|
+
name = "testdb"
|
|
89
134
|
connector = pst.PasConnector(name, "./tests/data/pas")
|
|
90
135
|
else:
|
|
91
136
|
raise ValueError("Unrecognized parameter!")
|
|
92
|
-
pstore = initialize_project(connector)
|
|
137
|
+
pstore = initialize_project(connector, data1, data2, data3)
|
|
93
138
|
pstore.type = request.param # added here for defining test dependencies
|
|
94
139
|
yield pstore
|
|
95
140
|
pst.util.delete_pastastore(pstore)
|
|
96
141
|
|
|
97
142
|
|
|
98
|
-
def delete_arcticdb_test_db():
|
|
99
|
-
connstr = "lmdb://./arctic_db/"
|
|
100
|
-
name = "test_project"
|
|
101
|
-
connector = pst.ArcticDBConnector(name, connstr)
|
|
102
|
-
pst.util.delete_arcticdb_connector(connector)
|
|
103
|
-
|
|
104
|
-
|
|
105
143
|
_has_pkg_cache = {}
|
|
106
144
|
|
|
107
145
|
|
|
@@ -158,3 +196,67 @@ def requires_pkg(*pkgs):
|
|
|
158
196
|
reason=f"missing package{'s' if len(missing) != 1 else ''}: "
|
|
159
197
|
+ ", ".join(missing),
|
|
160
198
|
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def for_connectors(connectors=None):
|
|
202
|
+
"""Decorate to run tests only for specified connector types.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
connectors : list of str, optional
|
|
207
|
+
List of connector types for which the test should run.
|
|
208
|
+
If None, defaults to all connector types: ["dict", "pas", "arcticdb"].
|
|
209
|
+
|
|
210
|
+
Returns
|
|
211
|
+
-------
|
|
212
|
+
function
|
|
213
|
+
Decorated test function that runs only for specified connectors.
|
|
214
|
+
"""
|
|
215
|
+
if connectors is None:
|
|
216
|
+
connectors = ["dict", "pas", "arcticdb"]
|
|
217
|
+
|
|
218
|
+
def decorator(func):
|
|
219
|
+
def wrapper(self, *args, **kwargs):
|
|
220
|
+
# Get the connector type from fixtures passed
|
|
221
|
+
# Could be in args or kwargs depending on how pytest passes it
|
|
222
|
+
store = None
|
|
223
|
+
|
|
224
|
+
# Check kwargs first (fixture passed by name)
|
|
225
|
+
for fixture_name in ["pstore_with_models", "pstore", "conn"]:
|
|
226
|
+
if fixture_name in kwargs:
|
|
227
|
+
store = kwargs[fixture_name]
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
# If not in kwargs, check first positional arg
|
|
231
|
+
if store is None and len(args) > 0:
|
|
232
|
+
store = args[0]
|
|
233
|
+
|
|
234
|
+
if store is None:
|
|
235
|
+
# Call without checking (shouldn't happen)
|
|
236
|
+
return func(self, *args, **kwargs)
|
|
237
|
+
|
|
238
|
+
# Check if we should skip based on connector type
|
|
239
|
+
if hasattr(store, "conn"):
|
|
240
|
+
conn_type = store.conn.conn_type
|
|
241
|
+
elif hasattr(store, "conn_type"):
|
|
242
|
+
conn_type = store.conn_type
|
|
243
|
+
else:
|
|
244
|
+
# If we can't determine connector type, just run the test
|
|
245
|
+
return func(self, *args, **kwargs)
|
|
246
|
+
|
|
247
|
+
if conn_type not in connectors:
|
|
248
|
+
pytest.skip(
|
|
249
|
+
f"Test skipped for {conn_type} connector "
|
|
250
|
+
f"(only runs for: {connectors})"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
return func(self, *args, **kwargs)
|
|
254
|
+
|
|
255
|
+
# Preserve the original function signature and metadata for pytest
|
|
256
|
+
wrapper.__signature__ = inspect.signature(func)
|
|
257
|
+
wrapper.__name__ = func.__name__
|
|
258
|
+
wrapper.__doc__ = func.__doc__
|
|
259
|
+
|
|
260
|
+
return wrapper
|
|
261
|
+
|
|
262
|
+
return decorator
|
tests/test_003_pastastore.py
CHANGED
|
@@ -39,9 +39,11 @@ def test_get_tmintmax(pstore):
|
|
|
39
39
|
|
|
40
40
|
@pytest.mark.dependency
|
|
41
41
|
def test_search(pstore):
|
|
42
|
-
results = pstore.search("
|
|
42
|
+
results = pstore.search("OSER", libname="oseries", case_sensitive=False)
|
|
43
43
|
assert len(results) == 3
|
|
44
44
|
assert len(set(results) - {"oseries1", "oseries2", "oseries3"}) == 0
|
|
45
|
+
results = pstore.search("oser", libname=None, case_sensitive=True)
|
|
46
|
+
assert len(results["oseries"]) == 3
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
@pytest.mark.dependency
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# ruff: noqa: D100 D103
|
|
2
|
+
import pytest
|
|
3
|
+
from conftest import for_connectors
|
|
4
|
+
|
|
5
|
+
import pastastore as pst
|
|
6
|
+
|
|
7
|
+
pst.get_color_logger("DEBUG", logger_name="pastastore")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(scope="module")
|
|
11
|
+
def pstore_with_models(pstore):
|
|
12
|
+
"""
|
|
13
|
+
Fixture that creates models for oseries1 and oseries2.
|
|
14
|
+
|
|
15
|
+
This provides a pastastore with two models ready for testing parallel-safe
|
|
16
|
+
operations and time series link updates.
|
|
17
|
+
"""
|
|
18
|
+
# Create and add model for oseries1
|
|
19
|
+
ml1 = pstore.create_model("oseries1")
|
|
20
|
+
ml1.solve(report=False)
|
|
21
|
+
pstore.add_model(ml1)
|
|
22
|
+
|
|
23
|
+
# Create and add model for oseries2
|
|
24
|
+
ml2 = pstore.create_model("oseries2")
|
|
25
|
+
ml2.solve(report=False)
|
|
26
|
+
pstore.add_model(ml2)
|
|
27
|
+
# trigger update to start fresh
|
|
28
|
+
_ = pstore.conn.oseries_models
|
|
29
|
+
# ensure clean start
|
|
30
|
+
pstore.conn._trigger_links_update_if_needed()
|
|
31
|
+
yield pstore
|
|
32
|
+
|
|
33
|
+
# Cleanup
|
|
34
|
+
if "oseries1" in pstore.model_names:
|
|
35
|
+
pstore.del_model("oseries1")
|
|
36
|
+
if "oseries2" in pstore.model_names:
|
|
37
|
+
pstore.del_model("oseries2")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestSingleThreadOperations:
|
|
41
|
+
"""Test single-thread add_model operations for all connector types."""
|
|
42
|
+
|
|
43
|
+
def test_oseries_models_triggers_update(self, pstore_with_models):
|
|
44
|
+
"""Test that accessing oseries_models triggers update of added models."""
|
|
45
|
+
# Initially, _added_models should be empty
|
|
46
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
47
|
+
# add model
|
|
48
|
+
ml = pstore_with_models.create_model("oseries3")
|
|
49
|
+
pstore_with_models.add_model(ml)
|
|
50
|
+
# Access oseries_models to trigger update
|
|
51
|
+
_ = pstore_with_models.conn.oseries_models
|
|
52
|
+
# After update, _added_models should be empty
|
|
53
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
54
|
+
# cleanup
|
|
55
|
+
pstore_with_models.del_model("oseries3")
|
|
56
|
+
|
|
57
|
+
def test_stresses_models_triggers_update(self, pstore_with_models):
|
|
58
|
+
"""Test that accessing stresses_models triggers update of added models."""
|
|
59
|
+
# initially, _added_models should be empty
|
|
60
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
61
|
+
# add model
|
|
62
|
+
ml = pstore_with_models.create_model("oseries3")
|
|
63
|
+
pstore_with_models.add_model(ml)
|
|
64
|
+
# access stresses_models to trigger update
|
|
65
|
+
_ = pstore_with_models.conn.stresses_models
|
|
66
|
+
# after update, _added_models should be empty
|
|
67
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
68
|
+
# cleanup
|
|
69
|
+
pstore_with_models.del_model("oseries3")
|
|
70
|
+
|
|
71
|
+
def test_oseries_with_models_triggers_update(self, pstore_with_models):
|
|
72
|
+
"""Test that accessing oseries_with_models triggers update of added models."""
|
|
73
|
+
# initially, _added_models should be empty
|
|
74
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
75
|
+
# add model
|
|
76
|
+
ml = pstore_with_models.create_model("oseries3")
|
|
77
|
+
pstore_with_models.add_model(ml)
|
|
78
|
+
# access oseries_models to trigger update
|
|
79
|
+
_ = pstore_with_models.conn.oseries_with_models
|
|
80
|
+
# after update, _added_models should be empty
|
|
81
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
82
|
+
# cleanup
|
|
83
|
+
pstore_with_models.del_model("oseries3")
|
|
84
|
+
|
|
85
|
+
def test_stresses_with_models_triggers_update(self, pstore_with_models):
|
|
86
|
+
"""Test that accessing stresses_with_models triggers update of added models."""
|
|
87
|
+
# initially, _added_models should be empty
|
|
88
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
89
|
+
# add model
|
|
90
|
+
ml = pstore_with_models.create_model("oseries3")
|
|
91
|
+
pstore_with_models.add_model(ml)
|
|
92
|
+
# access stresses_with_models to trigger update
|
|
93
|
+
_ = pstore_with_models.conn.stresses_with_models
|
|
94
|
+
# after update, _added_models should be empty
|
|
95
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
96
|
+
# cleanup
|
|
97
|
+
pstore_with_models.del_model("oseries3")
|
|
98
|
+
|
|
99
|
+
def test_add_model_appends_to_added_models_list(self, pstore_with_models):
|
|
100
|
+
"""Test that add_model appends to _added_models list."""
|
|
101
|
+
# Check internal state, should contain two models
|
|
102
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
103
|
+
# Create a new model
|
|
104
|
+
ml = pstore_with_models.create_model("oseries3")
|
|
105
|
+
# Add model
|
|
106
|
+
pstore_with_models.add_model(ml)
|
|
107
|
+
# Verify the model name was added to _added_models
|
|
108
|
+
assert "oseries3" in pstore_with_models.conn._added_models
|
|
109
|
+
# check oseries_models (triggers link update)
|
|
110
|
+
assert "oseries3" in pstore_with_models.oseries_models
|
|
111
|
+
# check that _added_models is now empty after update
|
|
112
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
113
|
+
# Cleanup
|
|
114
|
+
pstore_with_models.del_model("oseries3")
|
|
115
|
+
|
|
116
|
+
def test_del_model_deletes_from_added_models_list(self, pstore_with_models):
|
|
117
|
+
"""Test that del_model deletes from non-empty _added_models list."""
|
|
118
|
+
# Check internal state, should be empty
|
|
119
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
120
|
+
# Create a new model
|
|
121
|
+
ml = pstore_with_models.create_model("oseries3")
|
|
122
|
+
# Add model
|
|
123
|
+
pstore_with_models.add_model(ml)
|
|
124
|
+
pstore_with_models.del_model("oseries3")
|
|
125
|
+
# check that _added_models is now empty after update
|
|
126
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class TestParallelButNotReally:
|
|
130
|
+
"""Test internal update flag behavior (for parallel)."""
|
|
131
|
+
|
|
132
|
+
@for_connectors(connectors=["pas", "arcticdb"])
|
|
133
|
+
def test_parallel_add_model_sets_update_flags(self, pstore_with_models):
|
|
134
|
+
"""Test parallel_safe adds set update flags for parallel connectors."""
|
|
135
|
+
|
|
136
|
+
def add_model(name):
|
|
137
|
+
"""Add model using global pstore."""
|
|
138
|
+
ml = pstore_with_models.get_models(name)
|
|
139
|
+
ml.solve(report=False)
|
|
140
|
+
pstore_with_models.add_model(ml, overwrite=True)
|
|
141
|
+
|
|
142
|
+
pstore_with_models.apply(
|
|
143
|
+
func=add_model,
|
|
144
|
+
names=["oseries1", "oseries2"],
|
|
145
|
+
libname="models",
|
|
146
|
+
parallel=False,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# check update parameters are set to True
|
|
150
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is True
|
|
151
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is True
|
|
152
|
+
|
|
153
|
+
# trigger update
|
|
154
|
+
pstore_with_models.conn._trigger_links_update_if_needed()
|
|
155
|
+
|
|
156
|
+
# after update, flags should be False
|
|
157
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is False
|
|
158
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is False
|
|
159
|
+
|
|
160
|
+
@for_connectors(connectors=["pas", "arcticdb"])
|
|
161
|
+
def test_oseries_models_triggers_update(self, pstore_with_models):
|
|
162
|
+
"""Test that accessing oseries_models triggers update of added models."""
|
|
163
|
+
# Initially, _added_models should be empty
|
|
164
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
165
|
+
|
|
166
|
+
def add_model(name):
|
|
167
|
+
"""Add model using pstore."""
|
|
168
|
+
ml = pstore_with_models.create_model(name)
|
|
169
|
+
pstore_with_models.add_model(ml, overwrite=True)
|
|
170
|
+
|
|
171
|
+
pstore_with_models.apply(
|
|
172
|
+
func=add_model,
|
|
173
|
+
names=["oseries1", "oseries2"],
|
|
174
|
+
libname="oseries",
|
|
175
|
+
parallel=False,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# check update parameters are set to True
|
|
179
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is True
|
|
180
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is True
|
|
181
|
+
|
|
182
|
+
# trigger update
|
|
183
|
+
om = pstore_with_models.oseries_models
|
|
184
|
+
|
|
185
|
+
# check if result is correct
|
|
186
|
+
assert om["oseries1"] == ["oseries1"]
|
|
187
|
+
assert om["oseries2"] == ["oseries2"]
|
|
188
|
+
|
|
189
|
+
# after update, flags should be False
|
|
190
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is False
|
|
191
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is False
|
|
192
|
+
|
|
193
|
+
@for_connectors(connectors=["pas", "arcticdb"])
|
|
194
|
+
def test_stresses_models_triggers_update(self, pstore_with_models):
|
|
195
|
+
"""Test that accessing stresses_models triggers update of added models."""
|
|
196
|
+
# Initially, _added_models should be empty
|
|
197
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
198
|
+
|
|
199
|
+
def add_model(name):
|
|
200
|
+
"""Add model using pstore."""
|
|
201
|
+
ml = pstore_with_models.create_model(name)
|
|
202
|
+
pstore_with_models.add_model(ml, overwrite=True)
|
|
203
|
+
|
|
204
|
+
pstore_with_models.apply(
|
|
205
|
+
func=add_model,
|
|
206
|
+
names=["oseries1", "oseries2"],
|
|
207
|
+
libname="oseries",
|
|
208
|
+
parallel=False,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# check update parameters are set to True
|
|
212
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is True
|
|
213
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is True
|
|
214
|
+
|
|
215
|
+
# trigger update
|
|
216
|
+
sm = pstore_with_models.stresses_models
|
|
217
|
+
|
|
218
|
+
# check if result is correct
|
|
219
|
+
assert sm["prec1"] == ["oseries1"]
|
|
220
|
+
assert sm["prec2"] == ["oseries2"]
|
|
221
|
+
assert sm["evap1"] == ["oseries1"]
|
|
222
|
+
assert sm["evap2"] == ["oseries2"]
|
|
223
|
+
|
|
224
|
+
# after update, flags should be False
|
|
225
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is False
|
|
226
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is False
|
|
227
|
+
|
|
228
|
+
@for_connectors(connectors=["pas", "arcticdb"])
|
|
229
|
+
def test_oseries_with_models_triggers_update(self, pstore_with_models):
|
|
230
|
+
"""Test that accessing oseries_with_models triggers update of added models."""
|
|
231
|
+
# Initially, _added_models should be empty
|
|
232
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
233
|
+
|
|
234
|
+
def add_model(name):
|
|
235
|
+
"""Add model using pstore."""
|
|
236
|
+
ml = pstore_with_models.create_model(name)
|
|
237
|
+
pstore_with_models.add_model(ml, overwrite=True)
|
|
238
|
+
|
|
239
|
+
pstore_with_models.apply(
|
|
240
|
+
func=add_model,
|
|
241
|
+
names=["oseries1", "oseries2"],
|
|
242
|
+
libname="oseries",
|
|
243
|
+
parallel=False,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# check update parameters are set to True
|
|
247
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is True
|
|
248
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is True
|
|
249
|
+
|
|
250
|
+
# trigger update
|
|
251
|
+
owm = pstore_with_models.oseries_with_models
|
|
252
|
+
|
|
253
|
+
# check if result is correct
|
|
254
|
+
assert "oseries1" in owm
|
|
255
|
+
assert "oseries2" in owm
|
|
256
|
+
|
|
257
|
+
# after update, flags should be False
|
|
258
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is False
|
|
259
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is False
|
|
260
|
+
|
|
261
|
+
@for_connectors(connectors=["pas", "arcticdb"])
|
|
262
|
+
def test_stresses_with_models_triggers_update(self, pstore_with_models):
|
|
263
|
+
"""Test that accessing stresses_with_models triggers update of added models."""
|
|
264
|
+
# Initially, _added_models should be empty
|
|
265
|
+
assert len(pstore_with_models.conn._added_models) == 0
|
|
266
|
+
|
|
267
|
+
def add_model(name):
|
|
268
|
+
"""Add model using pstore."""
|
|
269
|
+
ml = pstore_with_models.create_model(name)
|
|
270
|
+
pstore_with_models.add_model(ml, overwrite=True)
|
|
271
|
+
|
|
272
|
+
pstore_with_models.apply(
|
|
273
|
+
func=add_model,
|
|
274
|
+
names=["oseries1", "oseries2"],
|
|
275
|
+
libname="oseries",
|
|
276
|
+
parallel=False,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# check update parameters are set to True
|
|
280
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is True
|
|
281
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is True
|
|
282
|
+
|
|
283
|
+
# check if result is correct
|
|
284
|
+
swm = pstore_with_models.stresses_with_models
|
|
285
|
+
|
|
286
|
+
assert "prec1" in swm
|
|
287
|
+
assert "prec2" in swm
|
|
288
|
+
assert "evap1" in swm
|
|
289
|
+
assert "evap2" in swm
|
|
290
|
+
|
|
291
|
+
# after update, flags should be False
|
|
292
|
+
assert pstore_with_models.conn._oseries_links_need_update.value is False
|
|
293
|
+
assert pstore_with_models.conn._stresses_links_need_update.value is False
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def add_model_pas(name):
|
|
297
|
+
"""Add model using pstore."""
|
|
298
|
+
ml = ppstore.create_model(name)
|
|
299
|
+
ppstore.add_model(ml, overwrite=True)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def add_model_arcticdb(name):
|
|
303
|
+
"""Add model using pstore."""
|
|
304
|
+
ppstore = pst.PastaStore(conn)
|
|
305
|
+
ml = ppstore.create_model(name)
|
|
306
|
+
conn.add_model(ml, overwrite=True)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def setup_parallel_pstore(conn_type, data1, data2):
|
|
310
|
+
global conn
|
|
311
|
+
global ppstore
|
|
312
|
+
|
|
313
|
+
if conn_type == "arcticdb":
|
|
314
|
+
name = "paralleldb"
|
|
315
|
+
uri = "lmdb://./tests/data/arcticdb/"
|
|
316
|
+
conn = pst.ArcticDBConnector(name, uri)
|
|
317
|
+
elif conn_type == "pas":
|
|
318
|
+
name = "paralleldb"
|
|
319
|
+
conn = pst.PasConnector(name, "./tests/data/pas")
|
|
320
|
+
else:
|
|
321
|
+
raise ValueError("Unrecognized parameter!")
|
|
322
|
+
|
|
323
|
+
ppstore = pst.PastaStore(conn)
|
|
324
|
+
# dataset 1
|
|
325
|
+
ppstore.add_oseries(data1["oseries1"], "oseries1", metadata=data1["oseries1_meta"])
|
|
326
|
+
ppstore.add_stress(
|
|
327
|
+
data1["prec1"], "prec1", kind="prec", metadata=data1["prec1_meta"]
|
|
328
|
+
)
|
|
329
|
+
ppstore.add_stress(
|
|
330
|
+
data1["evap1"], "evap1", kind="evap", metadata=data1["evap1_meta"]
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# dataset 2
|
|
334
|
+
ppstore.add_oseries(data2["oseries2"], "oseries2", metadata=data2["oseries2_meta"])
|
|
335
|
+
ppstore.add_stress(
|
|
336
|
+
data2["prec2"], "prec2", kind="prec", metadata=data2["prec2_meta"]
|
|
337
|
+
)
|
|
338
|
+
ppstore.add_stress(
|
|
339
|
+
data2["evap2"], "evap2", kind="evap", metadata=data2["evap2_meta"]
|
|
340
|
+
)
|
|
341
|
+
return ppstore
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@pytest.mark.parametrize("conn_type", ["pas", "arcticdb"])
|
|
345
|
+
def test_parallel_add_model(conn_type, data1, data2):
|
|
346
|
+
"""Test that accessing stresses_with_models triggers update of added models."""
|
|
347
|
+
ppstore = setup_parallel_pstore(conn_type, data1, data2)
|
|
348
|
+
|
|
349
|
+
if conn_type == "arcticdb":
|
|
350
|
+
func = add_model_arcticdb
|
|
351
|
+
elif conn_type == "pas":
|
|
352
|
+
func = add_model_pas
|
|
353
|
+
else:
|
|
354
|
+
raise ValueError("Unrecognized parameter!")
|
|
355
|
+
try:
|
|
356
|
+
# Initially, _added_models should be empty
|
|
357
|
+
assert len(ppstore.conn._added_models) == 0
|
|
358
|
+
|
|
359
|
+
ppstore.apply(
|
|
360
|
+
func=func,
|
|
361
|
+
names=["oseries1", "oseries2"],
|
|
362
|
+
libname="oseries",
|
|
363
|
+
parallel=True,
|
|
364
|
+
max_workers=1,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# check update parameters are set to False, since after parallel these are
|
|
368
|
+
# recomputed automatically
|
|
369
|
+
assert ppstore.conn._oseries_links_need_update.value is False
|
|
370
|
+
assert ppstore.conn._stresses_links_need_update.value is False
|
|
371
|
+
|
|
372
|
+
# check if result is correct
|
|
373
|
+
om = ppstore.oseries_models
|
|
374
|
+
owm = ppstore.oseries_with_models
|
|
375
|
+
sm = ppstore.stresses_models
|
|
376
|
+
swm = ppstore.stresses_with_models
|
|
377
|
+
assert "oseries1" in owm
|
|
378
|
+
assert "oseries2" in owm
|
|
379
|
+
assert "oseries1" in om
|
|
380
|
+
assert "oseries2" in om
|
|
381
|
+
assert "oseries1" in om["oseries1"]
|
|
382
|
+
assert "oseries2" in om["oseries2"]
|
|
383
|
+
assert "prec1" in swm
|
|
384
|
+
assert "prec2" in swm
|
|
385
|
+
assert "evap1" in swm
|
|
386
|
+
assert "evap2" in swm
|
|
387
|
+
assert "oseries1" in sm["prec1"]
|
|
388
|
+
assert "oseries2" in sm["prec2"]
|
|
389
|
+
assert "oseries1" in sm["evap1"]
|
|
390
|
+
assert "oseries2" in sm["evap2"]
|
|
391
|
+
|
|
392
|
+
finally:
|
|
393
|
+
pst.util.delete_pastastore(ppstore)
|