|
45 | 45 | % |
46 | 46 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
47 | 47 |
|
48 | | - if nargin < 3 |
49 | | - dim_to_find = 'time'; |
50 | | - end |
51 | | - if nargin < 2 |
52 | | - error('n_samples is required'); |
53 | | - end |
| 48 | +arguments |
| 49 | + ds (1,1) struct |
| 50 | + n_samples (1,1) double {mustBePositive, mustBeInteger} |
| 51 | + dim_to_find {mustBeTextScalar} = 'time' |
| 52 | +end |
54 | 53 |
|
55 | 54 | ds_out = ds; % Start with a copy of the input |
| 55 | + |
| 56 | + % Store n_bin in attrs for compatibility with turbulence functions |
| 57 | + if ~isfield(ds_out, 'attrs') |
| 58 | + ds_out.attrs = struct(); |
| 59 | + end |
| 60 | + ds_out.attrs.n_bin = n_samples; |
56 | 61 |
|
57 | 62 | % Validate dimension name exists in at least one field |
58 | 63 | dim_exists = false; |
|
124 | 129 | inv_perm_order(perm_order) = 1:length(perm_order); |
125 | 130 | ds_out.(current_field).data = permute(reshape(mean_data, [n_bins sz(perm_order(2:end))]), inv_perm_order); |
126 | 131 |
|
| 132 | + % Calculate standard deviation for velocity fields (following Python DOLfYN) |
| 133 | + if strcmp(current_field, 'vel') || contains(current_field, 'vel') |
| 134 | + % Calculate standard deviation along first dimension |
| 135 | + std_data = squeeze(std(reshaped, 0, 1, 'omitnan')); |
| 136 | + |
| 137 | + % Create vel_std field |
| 138 | + std_field_name = [current_field '_std']; |
| 139 | + ds_out.(std_field_name) = ds_out.(current_field); % Copy structure |
| 140 | + ds_out.(std_field_name).data = permute(reshape(std_data, [n_bins sz(perm_order(2:end))]), inv_perm_order); |
| 141 | + ds_out.(std_field_name).long_name = 'Velocity Standard Deviation'; |
| 142 | + ds_out.(std_field_name).description = 'Standard deviation calculated during ensemble averaging'; |
| 143 | + end |
| 144 | + |
127 | 145 | % Update coordinates for this dimension if they exist |
128 | 146 | if isfield(ds.(current_field), 'coords') |
129 | 147 | coord_fields = fieldnames(ds.(current_field).coords); |
130 | 148 | for k = 1:length(coord_fields) |
131 | 149 | coord_field = coord_fields{k}; |
132 | 150 | if contains(lower(coord_field), lower(dim_to_find)) |
133 | 151 | coord_data = ds.(current_field).coords.(coord_field); |
134 | | - ds_out.(current_field).coords.(coord_field) = coord_data(1:n_samples:usable_samples); |
| 152 | + % For numeric data (like Unix timestamps), take arithmetic mean of each bin |
| 153 | + % This matches Python's sequential chunking with mean per bin |
| 154 | + coord_reshaped = reshape(coord_data(1:usable_samples), n_samples, n_bins); |
| 155 | + ds_out.(current_field).coords.(coord_field) = mean(coord_reshaped, 1, 'omitnan')'; |
135 | 156 | end |
136 | 157 | end |
137 | 158 | end |
|
147 | 168 | if contains(lower(coord_fields{i}), lower(dim_to_find)) |
148 | 169 | coord_data = ds.coords.(coord_fields{i}); |
149 | 170 | usable_samples = floor(length(coord_data)/n_samples) * n_samples; |
150 | | - ds_out.coords.(coord_fields{i}) = coord_data(1:n_samples:usable_samples); |
| 171 | + n_bins = usable_samples / n_samples; |
| 172 | + % For numeric data (like Unix timestamps), take arithmetic mean of each bin |
| 173 | + % This matches Python's sequential chunking with mean per bin |
| 174 | + coord_reshaped = reshape(coord_data(1:usable_samples), n_samples, n_bins); |
| 175 | + ds_out.coords.(coord_fields{i}) = mean(coord_reshaped, 1, 'omitnan')'; |
151 | 176 | end |
152 | 177 | end |
153 | 178 | end |
| 179 | + |
| 180 | + % Calculate U_std from horizontal velocity components (following Python DOLfYN) |
| 181 | + if isfield(ds_out, 'vel') && isfield(ds_out, 'vel_std') |
| 182 | + % Calculate horizontal velocity magnitude standard deviation |
| 183 | + if size(ds_out.vel.data, 3) >= 2 % Check we have at least u and v components |
| 184 | + u_mean = ds_out.vel.data(:, :, 1); |
| 185 | + v_mean = ds_out.vel.data(:, :, 2); |
| 186 | + u_std = ds_out.vel_std.data(:, :, 1); |
| 187 | + v_std = ds_out.vel_std.data(:, :, 2); |
| 188 | + |
| 189 | + % Calculate U_mag standard deviation using error propagation formula |
| 190 | + % U_std = sqrt((u*u_std)^2 + (v*v_std)^2) / U_mag |
| 191 | + u_mag = sqrt(u_mean.^2 + v_mean.^2); |
| 192 | + u_std_mag = sqrt((u_mean .* u_std).^2 + (v_mean .* v_std).^2) ./ u_mag; |
| 193 | + |
| 194 | + % Handle division by zero |
| 195 | + u_std_mag(u_mag == 0) = 0; |
| 196 | + |
| 197 | + % Create U_std field |
| 198 | + ds_out.U_std = struct(); |
| 199 | + ds_out.U_std.data = single(u_std_mag); |
| 200 | + ds_out.U_std.dims = ds_out.vel.dims(1:2); % Remove direction dimension |
| 201 | + ds_out.U_std.coords = ds_out.vel.coords; |
| 202 | + ds_out.U_std.coords = rmfield(ds_out.U_std.coords, 'dir'); % Remove dir coordinate |
| 203 | + ds_out.U_std.units = "m s-1"; |
| 204 | + ds_out.U_std.long_name = "Water Velocity Standard Deviation"; |
| 205 | + ds_out.U_std.description = 'Horizontal velocity magnitude standard deviation from ensemble averaging'; |
| 206 | + end |
| 207 | + end |
154 | 208 | end |
0 commit comments