1+ # ===================================================================
2+ # Copyright 2025 National Oceanography Centre
3+ # Licensed under the Apache License, Version 2.0 (the "License");
4+ # you may not use this file except in compliance with the License.
5+ # You may obtain a copy of the License at
6+ # http://www.apache.org/licenses/LICENSE-2.0.
7+ #
8+ # Unless required by applicable law or agreed to in writing, software
9+ # distributed under the License is distributed on an "AS IS" BASIS,
10+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11+ # implied. See the License for the specific language governing
12+ # permissions and limitations under the License.
13+ # ===================================================================
14+ """
15+ test_sampler.py
16+
17+ Description:
18+ This module includes unit tests for extracting profiles.
19+
20+ Author:
21+ Benjamin Barton (benbar@noc.ac.uk)
22+ """
23+ import pytest
24+ import datetime as dt
25+ import numpy as np
26+ import xarray as xr
27+ from OceanOSSE .sampling .sampler_nearest_neighbour import NNSampler
28+
29+ def test_sampler ():
30+ """
31+ Tests for extracting a profile that falls on a model grid point.
32+ """
33+ # Build dataset
34+ ds = construct_ds ()
35+
36+ # Synthetic profile
37+ prof_id = np .array ([0 ])
38+ profile_lon = np .array ([3 ])
39+ profile_lat = np .array ([5 ])
40+ profile = xr .Dataset (
41+ {
42+ "lon" : (("prof_id" ), profile_lon ),
43+ "lat" : (("prof_id" ), profile_lat )
44+ },
45+ coords = {
46+ "profile_id" : prof_id ,
47+ },
48+ )
49+
50+ sampler = NNSampler ()
51+ model_t = sampler .sample (ds , profile )
52+
53+ assert (model_t .votemper .to_numpy ().squeeze () == ds .votemper [:, 5 , 3 ]).all ()
54+
55+
56+ def test_sampler_multi ():
57+ """
58+ Tests for extracting multiple profiles that falls on a model grid point.
59+ """
60+ # Build dataset
61+ ds = construct_ds ()
62+
63+ # Synthetic profile
64+ prof_id = np .array ([0 , 1 ])
65+ profile_lon = np .array ([3 , 8 ])
66+ profile_lat = np .array ([5 , 6 ])
67+ profile = xr .Dataset (
68+ {
69+ "lon" : (("profile_id" ), profile_lon ),
70+ "lat" : (("profile_id" ), profile_lat )
71+ },
72+ coords = {
73+ "profile_id" : prof_id ,
74+ },
75+ )
76+
77+ sampler = NNSampler ()
78+ model_t = sampler .sample (ds , profile )
79+
80+ assert (model_t .votemper .isel (profile_id = 1 ) == ds .votemper [:, 6 , 8 ]).all ()
81+
82+
83+ def test_sampler_nn ():
84+ """
85+ Test for extracting a profile that falls between model grid points that will use nearest
86+ neighbour against analytic form.
87+ """
88+ # Build dataset
89+ ds = construct_ds ()
90+
91+ # Synthetic profile
92+ prof_id = np .array ([0 , 1 ])
93+ profile_lon = np .array ([3.5 , 1.2 ])
94+ profile_lat = np .array ([5.5 , 2.2 ])
95+ profile = xr .Dataset (
96+ {
97+ "lon" : (("profile_id" ), profile_lon ),
98+ "lat" : (("profile_id" ), profile_lat )
99+ },
100+ coords = {
101+ "profile_id" : prof_id ,
102+ },
103+ )
104+
105+ sampler = NNSampler ()
106+ model_t = sampler .sample (ds , profile )
107+ print (model_t )
108+
109+ assert ((model_t .votemper .isel (profile_id = 0 ) == ds .votemper [:, 6 , 4 ]).all ()
110+ & (model_t .votemper .isel (profile_id = 1 ) == ds .votemper [:, 2 , 1 ]).all ())
111+
112+
113+ def construct_ds ():
114+ """
115+ Build a dataset for testing.
116+ """
117+ lat = np .arange (0 , 8 )
118+ lon = np .arange (0 , 10 )
119+ depth = np .arange (0 , 150 , 10 )
120+
121+ # Broadcast to 3D (depth, lat, lon)
122+ d , y , x = np .meshgrid (depth , lat , lon , indexing = 'ij' )
123+
124+ # Synthetic temperature field
125+ votemper = 15 - (y * 0.4 ) + (x * 0.2 ) - (d * 0.02 )
126+
127+ # Build dataset
128+ ds = xr .Dataset (
129+ {
130+ "votemper" : (("d" , "j" , "i" ), votemper ),
131+ "lat" : (("j" , "i" ), y [0 , :, :]),
132+ "lon" : (("j" , "i" ), x [0 , :, :]),
133+ "depth" : (("d" , "j" , "i" ), d )
134+ },
135+ coords = {
136+ "d" : depth ,
137+ "j" : lat ,
138+ "i" : lon ,
139+ },
140+ )
141+
142+ return ds
0 commit comments