classdef sfield
% SFIELD is a class to describe scalar field.
% SFIELD is an object that contains information of a scalar field
% derived from a vector field (VFIELD) object. The class is part of
% the VECTOR FIELD TOOLBOX.
%
% SF_OBJ = SFIELD(VF_OBJ, FIELD_NAME) derive the required FIELD_NAME
% scalar field from the vecrtor field (VFIELD). SF_OBJ inherits the
% following properties from the VF_OBJ: X, Y, NAMET, NAMEX, NAMEY,
% SAMPLING RATE, SIZE, UNITT, UNITX, UNITY and VECTORTYPE.
%
% Available fields are:
% 'AbsCurl' : absolute value of the curl field
% 'Curl' : angular velocity of a vector field
% 'Divergence' : divergence of a vector field
% 'Energy' : kinematic energy of the vector field
% 'Enstrophy' : integral of the square of the vorticity
% 'Q' : Q method
% 'SquaredStrainRate2d' : the rate of change in strain with respect to time
% 'VelocityAngleDeg' : angle (DEG) of each velocity vector
% 'VelocityAngleRad' : angle (RAD) of each velocity vector
% 'VelocityNorm' : norm of the velocity vector [sqrt(u.^2 + v.^2)]
% 'VelocityX' : x-component of the vector field (u)
% 'VelocityY' : y-component of the vector field (v)
%
% To derive negative value of the scalar field, just add '-' in front
% of the field name (e.g. '-AbsCurl', '-VelocityX').
%
% Other names for the field are available but will be removed in
% future version.
%
% See also: dataset, vfield, sensor, sensorarray
properties
x = []; % xTickLabel coming from vField data (Mx1)
y = []; % yTickLabel coming from vField data (Nx1)
t = []; % zTickLabel coming from vFiedl data (Lx1);
val = []; % field value (NxMxT)
mask = []; % Mask for velocity field data
fieldInfo = struct(...
'fieldName', '', ... % Name of the scalar field
'fieldLabel', '', ... % Label for DATASET oject
'fieldUnit', '', ... % Unit of measure for the x-component of the velocity field
'namet', '', ... % t-axis name
'namex', '', ... % x-axis name
'namey', '', ... % y-axis name
'samplingRate', NaN, ... % Sampling rate
'size', [], ... % Field size (ROWS x COLS x TIME)
'unitt', '', ... % Time unit of measure.
'unitx', '', ... % x-axis unit of measure
'unity', '', ... % y-axis unit of measure
'vectorType', [] ... % Vector type (0: original | 1: interpolated)
);
end
methods
function sf_obj = sfield(varargin)
% SF_OBJ = SFIELD(VF_OBJ, FIELD_NAME)
option = sf_obj.sf_parseInput(varargin{:});
if ~isempty(option.vField)
% A VFIELD is provided
u = option.vField.u;
v = option.vField.v;
% Convert masked value to NaN
u(option.vField.mask == 1) = NaN;
v(option.vField.mask == 1) = NaN;
% Determin unit of measure scale factor
[scale,sf_unkown] = sf_obj.sf_getScaleFactor(option.vField);
% Determin the time unit
if sf_unkown
time_unit = ['(' option.vField.fieldInfo.unitx '/(' option.vField.fieldInfo.unitu '))'];
else
time_unit = option.vField.fieldInfo.unitt;
end
switch strtok(lower(option.mode),'+-')
case {'velocityx','ux','vx','u'}
sf_obj.val = u;
% Units and names
sf_obj.fieldInfo.fieldName = option.vField.fieldInfo.nameu;
sf_obj.fieldInfo.fieldLabel = 'VelocityX';
sf_obj.fieldInfo.fieldUnit = option.vField.fieldInfo.unitu;
case {'velocityy','uy','vy','v'}
sf_obj.val = v;
% Units and names
sf_obj.fieldInfo.fieldName = option.vField.fieldInfo.namev;
sf_obj.fieldInfo.fieldLabel = 'VelocityY';
sf_obj.fieldInfo.fieldUnit = option.vField.fieldInfo.unitv;
case {'velocitynorm','norm','magnitude'}
sf_obj.val = sqrt(u.^2 + v.^2);
% Units and names
sf_obj.fieldInfo.fieldName = ['sqrt(' option.vField.fieldInfo.nameu '^2 + ' option.vField.fieldInfo.namev '^2)'];
sf_obj.fieldInfo.fieldLabel = 'VelocityNorm';
sf_obj.fieldInfo.fieldUnit = option.vField.fieldInfo.unitv;
case {'energy','ken','en'}
sf_obj.val = .5*(u.^2 + v.^2);
% Units and names
sf_obj.fieldInfo.fieldName = 'Energy';
sf_obj.fieldInfo.fieldLabel = sf_obj.fieldInfo.fieldName;
sf_obj.fieldInfo.fieldUnit = ['(' option.vField.fieldInfo.nameu ')^2'];
case {'velocityanglerad','angle','rad'}
sf_obj.val = mod(atan2(v,u),2*pi);
% Units and names
sf_obj.fieldInfo.fieldName = 'Velocity angle';
sf_obj.fieldInfo.fieldLabel = 'VelocityAngleRad';
sf_obj.fieldInfo.fieldUnit = 'rad';
case {'velocityangledeg','deg'}
sf_obj.val = mod(atan2(v,u),2*pi)*180/pi;
% Units and names
sf_obj.fieldInfo.fieldName = 'Velocity angle';
sf_obj.fieldInfo.fieldLabel = 'VelocityAngleDef';
sf_obj.fieldInfo.fieldUnit = 'deg';
case {'curl','rot','vort'}
[mx,my] = meshgrid(option.vField.x,option.vField.y);
for k = 1:option.vField.fieldInfo.size(3)
sf_obj.val(:,:,k) = 2*curl(mx,my,u(:,:,k),v(:,:,k)).*scale;
end
% Units and names
sf_obj.fieldInfo.fieldName = 'Curl';
sf_obj.fieldInfo.fieldLabel = sf_obj.fieldInfo.fieldName;
sf_obj.fieldInfo.fieldUnit = [time_unit '^{-1}'];
case {'abscurl','absrot','absvort'}
tmp = sfield(option.vField,'curl');
sf_obj.val = abs(tmp.val);
% Units and names
sf_obj.fieldInfo.fieldName = '|Curl|';
sf_obj.fieldInfo.fieldLabel = 'AbsCurl';
sf_obj.fieldInfo.fieldUnit = tmp.fieldInfo.fieldUnit;
case {'divergence','div'}
[mx,my] = meshgrid(option.vField.x,option.vField.y);
for k = 1:option.vField.fieldInfo.size(3)
sf_obj.val(:,:,k) = divergence(mx,my,u(:,:,k),v(:,:,k)).*scale;
end
% Units and names
sf_obj.fieldInfo.fieldName = 'Divergence';
sf_obj.fieldInfo.fieldLabel = sf_obj.fieldInfo.fieldName;
sf_obj.fieldInfo.fieldUnit = [time_unit '^{-1}'];
case {'enstrophy','ens'}
tmp = sfield(option.vField,'curl');
sf_obj.val = tmp.val.^2/2;
% Units and names
sf_obj.fieldInfo.fieldName = 'Enstrophy';
sf_obj.fieldInfo.fieldLabel = sf_obj.fieldInfo.fieldName;
sf_obj.fieldInfo.fieldUnit = [time_unit '^{-2}'];
case {'squaredstrainrate2d','eps','epsilon'}
for k = 1:option.vField.fieldInfo.size(3)
[dudx, dudy] = gradient(option.vField.u(:,:,k),...
abs(diff(option.vField.x(1:2))),abs(diff(option.vField.y(1:2))));
[dvdx, dvdy] = gradient(option.vField.v(:,:,k),...
abs(diff(option.vField.x(1:2))),abs(diff(option.vField.y(1:2))));
sf_obj.val(:,:,k) = (dudx.*scale).^2 + (dvdy.*scale).^2 + .5*(((dudy + dvdx).*scale).^2);
end
% Units and names
sf_obj.fieldInfo.fieldName = '2D Squared strain rate';
sf_obj.fieldInfo.fieldLabel = 'SquaredStrainRate2d';
sf_obj.fieldInfo.fieldUnit = [time_unit '^{-2}'];
case {'q'}
ens_tmp = sfield(option.vField,'ens');
eps_tmp = sfield(option.vField,'eps');
sf_obj.val = .5*(ens_tmp.val - eps_tmp.val);
% Units and names
sf_obj.fieldInfo.fieldName = 'Q';
sf_obj.fieldInfo.fieldLabel = sf_obj.fieldInfo.fieldName;
sf_obj.fieldInfo.fieldUnit = [time_unit '^{-2}'];
otherwise
error('vectorFieldTB:sfield:sfield:input', ...
[ '''' option.mode ''' is not a supported mode parameter.']);
end
% Set other fields
sf_obj.x = option.vField.x;
sf_obj.y = option.vField.y;
sf_obj.t = option.vField.t;
sf_obj.mask = option.vField.mask;
sf_obj.fieldInfo.namex = option.vField.fieldInfo.namex;
sf_obj.fieldInfo.namey = option.vField.fieldInfo.namey;
sf_obj.fieldInfo.namet = option.vField.fieldInfo.namet;
sf_obj.fieldInfo.samplingRate = option.vField.fieldInfo.samplingRate;
sf_obj.fieldInfo.size = option.vField.fieldInfo.size;
sf_obj.fieldInfo.unitt = time_unit;
sf_obj.fieldInfo.unitx = option.vField.fieldInfo.unitx;
sf_obj.fieldInfo.unity = option.vField.fieldInfo.unity;
sf_obj.fieldInfo.vectorType = option.vField.fieldInfo.vectorType;
if strfind(option.mode,'-')
sf_obj.val = -sf_obj.val;
sf_obj.fieldInfo.fieldName = ['-' sf_obj.fieldInfo.fieldName];
sf_obj.fieldInfo.fieldLabel = ['minus_' sf_obj.fieldInfo.fieldLabel];
end
else
error('vectorFieldTB:sfield:sfield:input', ...
'A VFIELD object is required.');
end
end
% End
function sf_index = end(sf_obj,position,~)
if isempty(sf_obj.val)
error('vectorFieldTB:sfield:end:noFieldFound',...
'The object is empty');
else
sf_index = sf_obj.fieldInfo.size(position);
end
end
% Struct
function sf_struct = struct(sf_obj)
% STRUCT returns the structured version of the SFIELD object
sf_fieldnames = fieldnames(sf_obj);
values = cell(length(sf_fieldnames), 1);
for k = 1:length(sf_fieldnames)
[values{k, :}] = sf_obj.(sf_fieldnames{k});
end
sf_struct = cell2struct(values, sf_fieldnames, 1);
end
end
end