Tools to grow your traffic
Rocket helps you learn why your competitors rank so high and what you need to do to outrank them.
Meet our intuitive platform
Self-Service Analytics or ad hoc reporting gives users the ability to develop rapid reports, empowering users to analyze their data.
Site Audit
Site Audit crawls all the pages it finds on your website – then provides an overall SEO health score, visualises key data in charts, flags all possible SEO issues and provides recommendations on how to fix them.
Have a huge website? Not an issue.
Learn MoreRank Tracker
We track your desktop and mobile keyword rankings from any location and plot your full ranking history on a handy graph.
You can set up automated ranking reports to be sent to your email address, so you’ll never forget to check your ranking progress.
Rank Tracker Tool98%
Average satisfaction rating received in the past year24/7
Our support team is a quick chat or email away — 24 hours a day220k+
Extension installs from the two major mobile app storesSEO solutions for every need
We build best-in-class SEO software for every situation, from our all-in-one SEO platform to tools for local SEO, enterprise SERP analytics, and a powerful API.
Rocket Local
Moz Local distributes your business information across the web for maximum search engine visibility.
-
Real-time distribution
-
Duplicate closure
-
Review management
Rocket Pro
Our SEO solution to help you rank higher, drive qualified traffic to your website, and run high-impact SEO campaigns.
-
Keyword & link research
-
Technical site audits
-
SEO insights & reporting
Rocket STAT
STAT offers serious SERP tracking for experts. Track thousands to millions of keywords across any location.
-
Daily tracking
-
Local and mobile SERPs
-
Competitor intelligence
The world's most accurate SEO data.
36.5 trillion
links indexed by Link Explorer with our tools500 million
keyword suggestions in Keyword Explorer250,000
local business listings optimized with Moz LocalRecommended by leading experts in marketing and SEO
Our products are loved by users worldwide
"We use Rocket mainly for its site explorer, and it’s immensely improved how we find link targets. We use it both for getting quick analysis of a site, as well as utilizing its extensive index when we want to dive deep."
- James Curran General Manager Spotify"We use Rocket mainly for its site explorer, and it’s immensely improved how we find link targets. We use it both for getting quick analysis of a site, as well as utilizing its extensive index when we want to dive deep."
- Richard Thomas Front-end developer Oracle"We use Rocket mainly for its site explorer, and it’s immensely improved how we find link targets. We use it both for getting quick analysis of a site, as well as utilizing its extensive index when we want to dive deep."
- Jose Evans Chief Engineer Apple"We use Rocket mainly for its site explorer, and it’s immensely improved how we find link targets. We use it both for getting quick analysis of a site, as well as utilizing its extensive index when we want to dive deep."
- Richard Thomas Designer Google


Mourinho rules out Tottenham move for Ibrahimovic
by Sports DeskJose Mourinho has ruled out a move for Zlatan Ibrahimovic because he has the "best striker in England" at his disposal in Harry Kane.
Ibrahimovic has been linked with a move to the north London club after Mourinho was swiftly announced as Mauricio Pochettino's replacement last week.
The pair briefly worked together at Inter during the 2008-09 season before linking up again at Manchester United in 2016.
Speaking ahead of his first Champions League game in charge of Spurs against Olympiacos on Tuesday, Mourinho poured cold water on suggestions he could swoop for the 38-year-old free agent.
"I have more than a connection [with Ibrahimovic]," he said. "He's an amazing player, amazing guy, but I would say no chance.
"We have the best striker in England. We have one of the top two or three strikers in the world.
"A striker of Zlatan's dimension...obviously in his late 30s but still a striker that can play at any club in the world.
"[But] it doesn't make sense to sign him when we have Harry Kane."
Tottenham confirmed Ben Davies is unavailable for Tuesday's game after sustaining an ankle injury against West Ham at the weekend, but Tanguy Ndombele and Jan Vertonghen both took part in training on Monday.
Spurs are five points behind Group B leaders Bayern Munich but will qualify for the last 16 if they overcome their Greek opponents at the Tottenham Hotspur Stadium.


Structure Management
Intro
A structure is a geometric object visualized in Polyscope, like a mesh or a point cloud. The first step in seeing your data in Polyscope is to register one or more structures to add them to the visualization. Then, quantities can be added to these structures, like scalar functions, colors, or vector fields.
Each structure should be given a name which is unique among structures of that type. You can then use this name as a handle to perform operations on the structure; For instance, you can register a mesh with:
polyscope::registerSurfaceMesh("my mesh", vertices, faces);
polyscope::getSurfaceMesh("my mesh")->addScalarQuantity("some values", values);
Memory management
As a general policy, Polyscope always manages its own memory, and will take care of deleting anything it allocated. Whenever a routine returns a pointer (like getStructure()
), it is a non-owning pointer. You should never delete
one of these pointers. To delete a structure and free memory, see the removeStructure()
methods below.
The following structures available in Polyscope. Many more structures are in development!
- Surface Mesh
- Point Cloud
- Curve Network
- Volume Mesh
- in progress: Camera View
Registering structures
Each structure offers a register___(name, ...)
function (like registerPointCloud()
) which accepts the name of the structure and the data necessary to construct it. These functions will return a Structure*
pointer which may be used to add quantities to the structure. See the relevant sections for documentation on each of these register functions.
The general form for registering structures is below; it may be useful if you are implementing your own structures.
bool registerStructure(Structure* structure, bool replaceIfPresent = true)
Register a new structure with Polyscope. The structure must have a Structure::name
which is unique amongst all registered structures of that type.
Polyscope takes ownership of the memory when the structure is registered, and will delete
it when no longer needed.
Note: most users will create structures via the individual registerPointCloud()
(etc) functions, rather than this general form.
Accessing structures
Polyscope offers two patterns for calling methods on a registered structure: you can either use the pointer returned after structure creation, or refer to the structure by name.
#include "polyscope/surface_mesh.h"
// register a structure
polyscope::SurfaceMesh* psMesh =
polyscope::registerSurfaceMesh("my mesh", vertices, faces);
// access with the pointer
psMesh->addScalarQuantity("some values", values);
// access by name
polyscope::getSurfaceMesh("my mesh")->addScalarQuantity("some values", values);
As before, each structure offers a get___(name)
method, like getSurfaceMesh(name)
which can be used to get a pointer to the structure of that type by name. The general form below may be useful if you are implementing your own structures.
Structure* getStructure(std::string type, std::string name = "")
Get a pointer to a registered structure. The type
must be the unique string corresponding to the structure type.
As a convenience, if the name
may be argument omitted only if there is exactly one structure of that type.
If not such structure is available, nullptr
will be returned.
Note: most users will get structures via the individual getPointCloud()
(etc) functions, rather than this general form.
Removing structures
If no longer needed, structures can be removed by name or by pointer. Removing a structure frees memory for the underlying objects, invalidating all references to the structure and its quantities.
void removeStructure(Structure* structure, bool errorIfAbsent = true)
Remove the specified structure and free objects associated with it.
If errorIfAbsent == true
, and error will be thrown if there is no such structure registered, otherwise the function will return silently.
void removeStructure(std::string type, std::string name, bool errorIfAbsent = true)
Identical to removeStructure(Struture*)
, but accepts a type name and name instead.
void removeStructure(std::string name, bool errorIfAbsent = true)
Identical to removeStructure(Struture*)
, but accepts a name instead. Will fail unless there is exactly one structure with the given name across all structure types.
Structure options
These basic options are shared by all structures. Structure options are managed as persistent values, and thus will persist if a new structure is registered with the same name.
Enabled
If a structure is disabled, it will be hidden from view, along with any quantities associated with that structure.
bool Structure::isEnabled()
Is the structure enabled?
void Structure::setEnabled(bool newVal)
Set the structure to be enabled or disabled.
Transparency
Set the transparency parameter for the structure. 1
is fully opaque (the default), and 0
is fully transparent. When the first structure has transparency applied, transparent rendering will be automatically enabled.
Transparency can be controlled in the UI via the structure’s [Options] --> [Transparency]
menu.
float Structure::getTransparency()
Get the transparency parameter for the structure.
void Structure::setTransparency(float alpha)
Set the transparancy for the structure.
Transforms
Each structure has an associated spatial transform applied to it for display in the scene. The transform encodes a translation, rotation, and scaling represented as a 4x4 homogeneous matrix. Initially this transformation is just the identity transform (it does nothing), but it can be adjusted to position the structures in your scene.
The transform can be controlled in the UI via the structure’s [Options] --> [Transform]
menu.
void Structure::centerBoundingBox()
Set the transformation such that the structure’s bounding box is centered at the world origin.
void Structure::rescaleToUnit()
Set the transformation scaling such that the structure has length scale 1. This makes all structures roughly the same size.
void Structure::resetTransform()
Reset the structure’s transform to be the identity transform (i.e. to do nothing).
void Structure::setTransform(glm::mat4x4 transform)
Set a particular transform matrix.
void Structure::setPosition(glm::vec3 vec)
Set the transformation matrix such that structure is transformed to the position vec
.
void Structure::translate(glm::vec3 vec)
Translate the transformation matrix by offset vec
.
glm::mat4x4 Structure::getTransform()
Get the current transformation matrix.
glm::vec3 Structure::getPosition()
Get the translation component of the transformation matrix, the position to which the structure’s origin is translated.
Slice planes
Options relating to slice planes which may be present in the scene.
Slice plane options can be controlled in the UI via the structure’s [Options] --> [Slice Planes]
menu.
Structure* Structure::setCullWholeElements(bool newVal)
If true
, slice planes will affect this structure by culling whole elements (tets, triangles, points, etc), rather than slicing through the middle of the elements.
Note that not all structures may support culling whole elements. If not supported, this setting will do nothing.
Default: false.
bool Structure::getCullWholeElements()
Get whether the cull whole elements setting is applied.
Structure* Structure::setIgnoreSlicePlane(std::string name, bool newValue)
Set a slice plane to be ignored by the structure. If newValue
is true
the slice plane will be ignored, and if false
it will be respected.
bool Structure::getIgnoreSlicePlane(std::string name)
Get if a slice plane is currently being ignored by the structure.


public static interface Protos.InitiateOrBuilder
extends com.google.protobuf.MessageOrBuilder
| Modifier and Type | Method and Description |
|---|---|
long |
getExpireTimeSecs()
required uint64 expire_time_secs = 3; |
long |
getMinAcceptedChannelSize()
required uint64 min_accepted_channel_size = 2; |
long |
getMinPayment()
required uint64 min_payment = 4; |
com.google.protobuf.ByteString |
getMultisigKey()
required bytes multisig_key = 1; |
boolean |
hasExpireTimeSecs()
required uint64 expire_time_secs = 3; |
boolean |
hasMinAcceptedChannelSize()
required uint64 min_accepted_channel_size = 2; |
boolean |
hasMinPayment()
required uint64 min_payment = 4; |
boolean |
hasMultisigKey()
required bytes multisig_key = 1; |
findInitializationErrors, getAllFields, getDefaultInstanceForType, getDescriptorForType, getField, getInitializationErrorString, getOneofFieldDescriptor, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, hasOneof
boolean hasMultisigKey()
required bytes multisig_key = 1;
This must be a raw pubkey in regular ECDSA form. Both compressed and non-compressed forms are accepted. It is used only in the creation of the multisig contract, as outputs are created entirely by the secondary
com.google.protobuf.ByteString getMultisigKey()
required bytes multisig_key = 1;
This must be a raw pubkey in regular ECDSA form. Both compressed and non-compressed forms are accepted. It is used only in the creation of the multisig contract, as outputs are created entirely by the secondary
boolean hasMinAcceptedChannelSize()
required uint64 min_accepted_channel_size = 2;
Once a channel is exhausted a new one must be set up. So secondary indicates the minimum size it's willing to accept here. This can be lower to trade off resources against security but shouldn't be so low the transactions get rejected by the network as spam. Zero isn't a sensible value to have here, so we make the field required.
long getMinAcceptedChannelSize()
required uint64 min_accepted_channel_size = 2;
Once a channel is exhausted a new one must be set up. So secondary indicates the minimum size it's willing to accept here. This can be lower to trade off resources against security but shouldn't be so low the transactions get rejected by the network as spam. Zero isn't a sensible value to have here, so we make the field required.
boolean hasExpireTimeSecs()
required uint64 expire_time_secs = 3;
Rough UNIX time for when the channel expires. This is determined by the block header timestamps which can be very inaccurate when miners use the obsolete RollNTime hack. Channels could also be specified in terms of block heights but then how do you know the current chain height if you don't have internet access? Trust the server? Probably opens up attack vectors. We can assume the client has an independent clock, however. If the client considers this value too far off (eg more than a day), it may send an ERROR and close the channel.
long getExpireTimeSecs()
required uint64 expire_time_secs = 3;
Rough UNIX time for when the channel expires. This is determined by the block header timestamps which can be very inaccurate when miners use the obsolete RollNTime hack. Channels could also be specified in terms of block heights but then how do you know the current chain height if you don't have internet access? Trust the server? Probably opens up attack vectors. We can assume the client has an independent clock, however. If the client considers this value too far off (eg more than a day), it may send an ERROR and close the channel.
boolean hasMinPayment()
required uint64 min_payment = 4;
The amount of money the server requires for the initial payment. The act of opening a channel always transfers some quantity of money to the server: it's impossible to have a channel with zero value transferred. This rule ensures that you can't get a channel that can't be settled due to having paid under the dust limit. Because the dust limit will float in future, the server tells the client what it thinks it is, and the client is supposed to sanity check this value.
long getMinPayment()
required uint64 min_payment = 4;
The amount of money the server requires for the initial payment. The act of opening a channel always transfers some quantity of money to the server: it's impossible to have a channel with zero value transferred. This rule ensures that you can't get a channel that can't be settled due to having paid under the dust limit. Because the dust limit will float in future, the server tells the client what it thinks it is, and the client is supposed to sanity check this value.
Copyright © 2022. All rights reserved.


Exploring Dev Tools will keep you busy for hours.
This tab will teach you so much about what's in requests and responses. Google everything you don't understand.


Extract river data from the supplied river positions for the FVCOM grid in Mobj from the NEMO data get_NEMO_rivers(Mobj, dist_thresh) DESCRIPTION: For the positions in Mobj.rivers.positions, find the nearest unstructured grid node and extract the river discharge from Mobj.rivers.river_flux. To correct the flux values from the NEMO data, we need the grid size data (in Mobj.rivers.river_coordinates). The river positions must fall within the specified distance (dist_thresh). INPUT: Mobj - MATLAB mesh object containing: * have_lonlat - boolean to check for spherical coordinates. * lon, lat - positions for the unstructured grid. * tri - triangulation table for the unstructured grid. * nVerts - number of nodes in the grid. * read_obc_nodes - open boundary node IDs. * rivers - river data struct with the following fields: - positions - river positions in lon, lat. - names - list of river names - river_flux - path to the NEMO netCDF data file. Must contain the area of each grid element for conversion of the fluxes. dist_thresh - maximum distance away from a river node beyond which the search for an FVCOM node is abandoned. Units in degrees. The following keyword-argument pairs are also valid: 'model_year' - [optional] when giving climatology, a year must be specified so that the time series can be anchored in time. The returned time series will be 3 years long centred on the specified year. Discharges will be repeated for the two additional years. 'dump_positions' - [optional] dump the NEMO river positions to the specified text file. 'alternate_positions' - [optional] read in a CSV file with alternate positions for the NEMO rivers. This is useful if you've manually moved the NEMO rivers onto more realistic locations. This assumes that the alternate positions supplied are in the same order as the original ones are read in. OUTPUT: Mobj.river_flux - volume flux at the nodes within the model domain. Mobj.river_nh4 - ammonia Mobj.river_no3 - notrate Mobj.river_o - oxygen Mobj.river_p - phosphate Mobj.river_sio3 - silicate Mobj.river_dic - dissolved inorganic carbon Mobj.river_totalk - total alkalinity Mobj.river_bioalk - bio-alkalinity Mobj.river_nodes - node IDs for the rivers. Mobj.river_names - river names which fall within the model domain. For rivers where the discharge has been summed, the name is compoud, with each contributing name separated by a hyphen (-). Mobj.river_time - Modified Julian Day time series for the river discharge data. EXAMPLE USAGE: Mobj = get_NEMO_rivers(Mobj, 0.15) To extract the NEMO river locations to file: Mobj = get_NEMO_rivers(Mobj, 0.15, 'dump_positions', '/tmp/nemo.txt'); Author(s): Pierre Cazenave (Plymouth Marine Laboratory) Revision history: 2016-03-02 - First version based on get_nemo_rivers.m. 2016-05-03 - Add option to dump NEMO river locations to text file. 2016-06-06 - Remove temperature and salinity from the description as they're really the Baltic Sea inputs only. Also read in the grid area from the new format ERSEM file (variable dA). Add total alkalinity to the outputs. 2016-08-10 - Add new option to use an alternative river positions. ==========================================================================
0001 function Mobj = get_NEMO_rivers(Mobj, dist_thresh, varargin) 0002 % Extract river data from the supplied river positions for the FVCOM 0003 % grid in Mobj from the NEMO data 0004 % 0005 % get_NEMO_rivers(Mobj, dist_thresh) 0006 % 0007 % DESCRIPTION: 0008 % For the positions in Mobj.rivers.positions, find the nearest 0009 % unstructured grid node and extract the river discharge from 0010 % Mobj.rivers.river_flux. To correct the flux values from the 0011 % NEMO data, we need the grid size data (in 0012 % Mobj.rivers.river_coordinates). The river positions must fall 0013 % within the specified distance (dist_thresh). 0014 % 0015 % INPUT: 0016 % Mobj - MATLAB mesh object containing: 0017 % * have_lonlat - boolean to check for spherical coordinates. 0018 % * lon, lat - positions for the unstructured grid. 0019 % * tri - triangulation table for the unstructured grid. 0020 % * nVerts - number of nodes in the grid. 0021 % * read_obc_nodes - open boundary node IDs. 0022 % * rivers - river data struct with the following fields: 0023 % - positions - river positions in lon, lat. 0024 % - names - list of river names 0025 % - river_flux - path to the NEMO netCDF data file. Must contain 0026 % the area of each grid element for conversion of the fluxes. 0027 % dist_thresh - maximum distance away from a river node beyond 0028 % which the search for an FVCOM node is abandoned. Units in degrees. 0029 % The following keyword-argument pairs are also valid: 0030 % 'model_year' - [optional] when giving climatology, a year must be 0031 % specified so that the time series can be anchored in time. The 0032 % returned time series will be 3 years long centred on the specified 0033 % year. Discharges will be repeated for the two additional years. 0034 % 'dump_positions' - [optional] dump the NEMO river positions to the 0035 % specified text file. 0036 % 'alternate_positions' - [optional] read in a CSV file with alternate 0037 % positions for the NEMO rivers. This is useful if you've manually 0038 % moved the NEMO rivers onto more realistic locations. This assumes 0039 % that the alternate positions supplied are in the same order as the 0040 % original ones are read in. 0041 % 0042 % OUTPUT: 0043 % Mobj.river_flux - volume flux at the nodes within the model domain. 0044 % Mobj.river_nh4 - ammonia 0045 % Mobj.river_no3 - notrate 0046 % Mobj.river_o - oxygen 0047 % Mobj.river_p - phosphate 0048 % Mobj.river_sio3 - silicate 0049 % Mobj.river_dic - dissolved inorganic carbon 0050 % Mobj.river_totalk - total alkalinity 0051 % Mobj.river_bioalk - bio-alkalinity 0052 % Mobj.river_nodes - node IDs for the rivers. 0053 % Mobj.river_names - river names which fall within the model domain. For 0054 % rivers where the discharge has been summed, the name is compoud, 0055 % with each contributing name separated by a hyphen (-). 0056 % Mobj.river_time - Modified Julian Day time series for the river 0057 % discharge data. 0058 % 0059 % EXAMPLE USAGE: 0060 % Mobj = get_NEMO_rivers(Mobj, 0.15) 0061 % 0062 % To extract the NEMO river locations to file: 0063 % 0064 % Mobj = get_NEMO_rivers(Mobj, 0.15, 'dump_positions', '/tmp/nemo.txt'); 0065 % 0066 % Author(s): 0067 % Pierre Cazenave (Plymouth Marine Laboratory) 0068 % 0069 % Revision history: 0070 % 2016-03-02 - First version based on get_nemo_rivers.m. 0071 % 2016-05-03 - Add option to dump NEMO river locations to text file. 0072 % 2016-06-06 - Remove temperature and salinity from the description as 0073 % they're really the Baltic Sea inputs only. Also read in the grid area 0074 % from the new format ERSEM file (variable dA). Add total alkalinity 0075 % to the outputs. 0076 % 2016-08-10 - Add new option to use an alternative river positions. 0077 % 0078 %========================================================================== 0079 0080 [~, subname] = fileparts(mfilename('fullpath')); 0081 0082 global ftbverbose 0083 if ftbverbose 0084 fprintf('\nbegin : %s\n', subname) 0085 end 0086 0087 % Check inputs 0088 if ~Mobj.have_lonlat 0089 error(['Require unstructured grid positions in lon/lat format ', ... 0090 'to compare against supplied river positions.']) 0091 end 0092 0093 yr = []; 0094 0095 % If we have only three arguments, we have to assume we've been given a 0096 % year for the climatology. Otherwise, we need to read the arguments based 0097 % on keyword-value pairs. Really, we want keyword-value pairs all the time, 0098 % so silently work when given three arguments and don't mention it in the 0099 % help. This is going to bite me at some point in the future, I'm sure. 0100 dump_positions = false; 0101 alt_positions = false; 0102 if nargin == 3 0103 yr = varargin{1}; 0104 elseif nargin > 3 0105 for aa = 1:2:length(varargin) 0106 switch varargin{aa} 0107 case 'model_year' 0108 yr = varargin{aa + 1}; 0109 case 'dump_positions' 0110 dump_positions = true; 0111 position_file = varargin{aa + 1}; 0112 case 'alternate_positions' 0113 alt_positions = true; 0114 alternate_file = varargin{aa + 1}; 0115 end 0116 end 0117 end 0118 0119 if ~isempty(yr) && ~isnumeric(yr) 0120 error('Trying to do climatology, but don''t have an anchor year. Supply one via the ''model_year'' keyword-value pair.') 0121 end 0122 0123 % Load the NEMO data. 0124 nemo.lon = ncread(Mobj.rivers.river_flux, 'x'); 0125 nemo.lat = ncread(Mobj.rivers.river_flux, 'y'); 0126 [nemo.LON, nemo.LAT] = meshgrid(nemo.lon, nemo.lat); 0127 nemo.time = ncread(Mobj.rivers.river_flux, 'time_counter'); 0128 nemo.flux = ncread(Mobj.rivers.river_flux, 'rorunoff'); 0129 nemo.nh4 = ncread(Mobj.rivers.river_flux, 'ronh4'); 0130 nemo.no3 = ncread(Mobj.rivers.river_flux, 'rono3'); 0131 nemo.o = ncread(Mobj.rivers.river_flux, 'roo'); 0132 nemo.p = ncread(Mobj.rivers.river_flux, 'rop'); 0133 nemo.sio3 = ncread(Mobj.rivers.river_flux, 'rosio2'); 0134 nemo.dic = ncread(Mobj.rivers.river_flux, 'rodic'); 0135 nemo.alt = ncread(Mobj.rivers.river_flux, 'rototalk'); 0136 nemo.bioalk = ncread(Mobj.rivers.river_flux, 'robioalk'); 0137 % nemo.temp = ncread(Mobj.rivers.river_flux, 'rotemper'); 0138 % nemo.salt = ncread(Mobj.rivers.river_flux, 'rosaline'); 0139 0140 % Now get the NEMO grid data. 0141 % nemo.e1t = ncread(Mobj.rivers.river_coordinates, 'e1t'); 0142 % nemo.e2t = ncread(Mobj.rivers.river_coordinates, 'e2t'); 0143 % Calculate the area for all elements in the grid. 0144 % nemo.area = nemo.e1t .* nemo.e2t; 0145 % Just use the new area variable instead of needing two files. 0146 nemo.area = ncread(Mobj.rivers.river_flux, 'dA'); 0147 0148 % NEMO does conversions to ERSEM units internally. Whilst this is easy in 0149 % some ways, it's not particularly transparent. So, instead, we'll do all 0150 % the conversions up front and then the data that get loaded into ERSEM are 0151 % already in the correct units. To summarise those conversions, we have: 0152 % 0153 % nutrients (nh4, no3, o, p, sio3) from grams/l to mmol/m^3 0154 % flux from kg/m^{2}/s to m^{3}/s (divide by freshwater density) 0155 % dic - no change whatsoever. 0156 0157 % Convert units from grams to millimoles where appropriate. 0158 nemo.nh4 = (nemo.nh4 / 14) * 1000; 0159 nemo.no3 = (nemo.no3 / 14) * 1000; 0160 nemo.o = (nemo.o / 32) * 1000; % 2 x 16 for O2 0161 nemo.p = (nemo.p / 35.5) * 1000; 0162 nemo.sio3 = (nemo.sio3 / 28) * 1000; 0163 0164 [~, ~, nt] = size(nemo.flux); 0165 0166 % Flux in NEMO is specified in kg/m^{2}/s. FVCOM wants m^{3}/s. Divide by 0167 % freshwater density to get m/s and then multiply by the area of each 0168 % element to get flux. 0169 nemo.flux = nemo.flux / 1000; 0170 % Now multiply by the relevant area to (finally!) get to m^{3}/s. 0171 nemo.flux = nemo.flux .* repmat(nemo.area, 1, 1, nt); 0172 0173 % Now we've got the data, use the flux data to find the indices of the 0174 % rivers in the arrays and extract those as time series in a format 0175 % suitable for writing out with write_FVCOM_river. 0176 mask = sum(nemo.flux, 3) ~= 0; 0177 % Get the indices so we can extract the time series. 0178 [mirow, micol] = ind2sub(size(mask), find(mask == true)); 0179 nr = length(mirow); 0180 0181 % Now do all the data. 0182 names = fieldnames(nemo); 0183 for n = 1:length(names) 0184 switch names{n} 0185 case {'lon', 'lat', 'LON', 'LAT', 'time'} 0186 continue 0187 end 0188 0189 % Preallocate and then fill with the time series for each valid river 0190 % location. 0191 nemo.rivers.(names{n}) = nan(nt, nr); 0192 for r = 1:nr 0193 nemo.rivers.(names{n})(:, r) = squeeze(nemo.(names{n})(mirow(r), micol(r), :)); 0194 end 0195 end 0196 0197 % Do the positions in the same way (with a loop) to avoid having to figure 0198 % out if using the mask collapses the arrays in teh same way as the find 0199 % did. 0200 nemo.rivers.positions = nan(nr, 2); 0201 nemo.rivers.names = {}; 0202 for r = 1:nr 0203 % For some reason, the output of meshgrid is the wrong way around, so 0204 % use the row and column indices the wrong way around too. 0205 nemo.rivers.positions(r, :) = [nemo.LON(micol(r), mirow(r)), ... 0206 nemo.LAT(micol(r), mirow(r))]; 0207 % We don't have sensible names for the NEMO rivers, so use the position 0208 % instead. Perhaps there exists a list of the names somewhere which I 0209 % could use. Yuri has asked Sarah Wakelin to see if she's got that 0210 % list. 0211 nemo.rivers.names{r, 1} = sprintf('river_%.6f-%.6f', ... 0212 nemo.rivers.positions(r, :)); 0213 end 0214 0215 if alt_positions 0216 % We've been given a file with alternate river positions in. Use that 0217 % for the river locations instead. 0218 f = open(alternate_positions, 'r'); 0219 0220 end 0221 0222 % Separate the inputs into separate arrays. 0223 nemo_name = nemo.rivers.names; 0224 nemo_xy = nemo.rivers.positions; 0225 0226 if dump_positions 0227 % Add a header for GIS 0228 dlmwrite(position_file, 'lonDD,latDD', 'delimiter', ''); 0229 dlmwrite(position_file, nemo_xy, 'precision', '%0.6f', '-append'); 0230 end 0231 0232 fv_nr = length(nemo_name); 0233 0234 % Check each location in the NEMO positions against the grid in Mobj and 0235 % for the indices within the dist_thresh, load and extract the relevant 0236 % time series data. 0237 0238 vc = 0; % valid FVCOM boundary node counter 0239 0240 % We need to find the unstructured grid boundary nodes and exclude the open 0241 % boundary nodes from them. This will be our list of potential candidates 0242 % for the river nodes (i.e. the land coastline). 0243 [~, ~, ~, bnd] = connectivity([Mobj.lon, Mobj.lat], Mobj.tri); 0244 boundary_nodes = 1:Mobj.nVerts; 0245 boundary_nodes = boundary_nodes(bnd); 0246 coast_nodes = boundary_nodes(~ismember(boundary_nodes, [Mobj.read_obc_nodes{:}])); 0247 tlon = Mobj.lon(coast_nodes); 0248 tlat = Mobj.lat(coast_nodes); 0249 0250 fv_obc = nan; 0251 fv_names = cell(0); 0252 0253 % Initialise the flow array with a 366 day long time series of nans. This 0254 % array will be appended to (unless all rivers are outside the domain). 0255 % Only do this if we're doing climatology (signified by a non-empty year). 0256 skipped = 0; 0257 0258 % Preallocate all the arrays. 0259 for n = 1:length(names) 0260 switch names{n} 0261 case {'lon', 'lat', 'LON', 'LAT', 'time'} 0262 continue 0263 end 0264 fv.(names{n}) = nan(nt, nr); 0265 end 0266 0267 for ff = 1:fv_nr 0268 % Find the coastline node closest to this river. Don't bother with sqrt 0269 % for the distance threshold, instead just square the distance when 0270 % doing the comparison. This should increase performance, although 0271 % probably only marginally. 0272 fv_dist = (nemo_xy(ff, 1) - tlon).^2 + ... 0273 (nemo_xy(ff, 2) - tlat).^2; 0274 [c, idx] = min(fv_dist); 0275 if c > dist_thresh^2 0276 skipped = skipped + 1; 0277 continue 0278 else 0279 if ftbverbose 0280 fprintf('candidate river %s found (%f, %f)... ', nemo_name{ff}, nemo_xy(ff, 1), nemo_xy(ff, 2)) 0281 end 0282 end 0283 0284 vc = vc + 1; 0285 0286 % We need to make sure the element in which this node occurs does not 0287 % have two land boundaries (otherwise the model just fills up that 0288 % element because that element will always have a zero velocity). 0289 0290 % Find the other nodes which are joined to the node we've just found. 0291 % We don't need the column to get the other nodes in the element, only 0292 % the row is required. 0293 [row, ~] = find(Mobj.tri == coast_nodes(idx)); 0294 0295 if length(row) == 1 0296 % This is a bad node because it is a part of only one element. The 0297 % rivers need two adjacent elements to work reliably (?). So, we 0298 % need to repeat the process above until we find a node that's 0299 % connected to two elements. We'll try the other nodes in the 0300 % current element before searching the rest of the coastline (which 0301 % is computationally expensive). 0302 0303 % Remove the current node index from the list of candidates (i.e. 0304 % leave only the two other nodes in the element). 0305 mask = Mobj.tri(row, :) ~= coast_nodes(idx); 0306 n_tri = Mobj.tri(row, mask); 0307 0308 % Remove values which aren't coastline values (we don't want to set 0309 % the river node to an open water node). 0310 n_tri = intersect(n_tri, coast_nodes); 0311 0312 % Of the remaining nodes in the element, find the closest one to 0313 % the original river location (in fvcom_xy). 0314 [~, n_idx] = sort(sqrt( ... 0315 (nemo_xy(ff, 1) - Mobj.lon(n_tri)).^2 ... 0316 + (nemo_xy(ff, 2) - Mobj.lon(n_tri)).^2)); 0317 0318 [row_2, ~] = find(Mobj.tri == n_tri(n_idx(1))); 0319 if length(n_idx) > 1 0320 [row_3, ~] = find(Mobj.tri == n_tri(n_idx(2))); 0321 end 0322 % Closest first 0323 if length(row_2) > 1 0324 idx = find(coast_nodes == n_tri(n_idx(1))); 0325 % The other one (only if we have more than one node to consider). 0326 elseif length(n_idx) > 1 && length(row_3) > 1 0327 idx = find(coast_nodes == n_tri(n_idx(2))); 0328 % OK, we need to search across all the other coastline nodes. 0329 else 0330 % TODO: Implement a search of all the other coastline nodes. 0331 % My testing indicates that we never get here (at least for the 0332 % grids I've tested). I'd be interested to see the mesh which 0333 % does get here... 0334 continue 0335 end 0336 if ftbverbose 0337 fprintf('alternate node ') 0338 end 0339 end 0340 0341 % Add it to the list of valid rivers 0342 fv_obc(vc) = coast_nodes(idx); 0343 fv_names{vc} = nemo.rivers.names{ff}; 0344 0345 % Add the current river data to the relevant arrays. 0346 for n = 1:length(names) 0347 switch names{n} 0348 case {'lon', 'lat', 'LON', 'LAT', 'time'} 0349 continue 0350 end 0351 fv.(names{n})(:, vc) = nemo.rivers.(names{n})(:, ff); 0352 end 0353 0354 if ftbverbose 0355 fprintf('added (%f, %f)\n', Mobj.lon(fv_obc(vc)), Mobj.lat(fv_obc(vc))) 0356 end 0357 0358 end 0359 0360 % Trim the data arrays for the rivers we've extracted since we preallocated 0361 % the array assuming we'd use all the rivers. 0362 for n = 1:length(names) 0363 switch names{n} 0364 case {'lon', 'lat', 'LON', 'LAT', 'time'} 0365 continue 0366 end 0367 fv.(names{n}) = fv.(names{n})(:, 1:vc); 0368 end 0369 0370 % Save a list of the field names in the FVCOM river data. 0371 fnames = fieldnames(fv); 0372 0373 % Assign the relevant arrays to the Mobj. Flux is added in the section 0374 % dealing with either climatology or time series data. 0375 Mobj.river_nodes = fv_obc; 0376 Mobj.river_names = fv_names'; 0377 Mobj.have_rivers = true; 0378 Mobj.nRivers = length(fv_obc); 0379 0380 % Dump the data into the relevant arrays in Mobj. 0381 for n = 1:length(fnames) 0382 new = sprintf('river_%s', fnames{n}); 0383 Mobj.(new) = fv.(fnames{n}); 0384 end 0385 0386 % Create a Modified Julian Day time series of the NEMO river data. Assume 0387 % it's a climatology, so generate the right time series based on that 0388 % assumption. 0389 if isempty(yr) && ~isnumeric(yr) 0390 error('For climatology, a year must be specified for the time series to be generated.') 0391 end 0392 0393 % Make three years of data starting from the year before the current one. 0394 % Do so accounting for leap years. 0395 daysinyr = [sum(eomday(yr - 1, 1:12)), ... 0396 sum(eomday(yr, 1:12)), ... 0397 sum(eomday(yr + 1, 1:12))]; 0398 % Offset the checkdate by one to add zero for the first day. Alternative 0399 % would be to specify the day as the end of the previous month (or a day 0400 % value of zero?). 0401 offsetdays = (1:sum(daysinyr)) - 1; 0402 mtime = datevec(datenum(yr - 1, 1, 1, 0, 0, 0) + offsetdays); 0403 Mobj.river_time = greg2mjulian(mtime(:, 1), mtime(:, 2), ... 0404 mtime(:, 3), mtime(:, 4), mtime(:, 5), mtime(:, 6)); 0405 0406 % Repeat the river data for the climatology before adding to the Mobj. 0407 for n = 1:length(fnames) 0408 new = sprintf('river_%s', fnames{n}); 0409 0410 % NEMO is insane. Old versions of the rivers data had enough days for 0411 % leap and non-leap years; new versions don't. So, let's pad the data 0412 % by a day in either case and that should word for data with both 365 0413 % and 366 times. In the case where we're using data with 366 values 0414 % already, this shouldn't make any difference as we're explicitly 0415 % indexing to the days in the year using daysinyr anyway. 0416 fv.(fnames{n}) = [fv.(fnames{n}); fv.(fnames{n})(end, :)]; 0417 0418 Mobj.(new) = [fv.(fnames{n})(1:daysinyr(1), :); ... 0419 fv.(fnames{n})(1:daysinyr(2), :); ... 0420 fv.(fnames{n})(1:daysinyr(3), :)]; 0421 end 0422 0423 if ftbverbose 0424 fprintf('included %d of %d rivers (skipped %d)\n', ... 0425 fv_nr - skipped, fv_nr, skipped) 0426 fprintf('end : %s \n', subname) 0427 end


Quests
Characters
Upload
How To
Log Out
Character Create
Description
Stats
Combat
Spells
Equipment
Features
Story
Description
How your character looks
Character Name
Race
Class
Background
Alignment
Level
Age
Height
Weight
Eyes
Skin
Hair
Character Appearance
Choose file
You can upload your own 150x50 pixel icon
Stats
Character stats
Strength
Dexterity
Constitution
Intelligence
Wisdom
Charisma
Acrobatics Pro and Modifier
Animal Handling Pro and Modifier
Arcana Pro and Modifier
Athletics Pro and Modifier
Deception Pro and Modifier
History Pro and Modifier
Insight Pro and Modifier
Intimidation Pro and Modifier
Investigation Pro and Modifier
Medicine Pro and Modifier
Nature Pro and Modifier
Perception Pro and Modifier
Performance Pro and Modifier
Persuasion Pro and Modifier
Religion Pro and Modifier
Sleight of Hand Pro and Modifier
Stealth Pro and Modifier
Survival Pro and Modifier
Saving Throw Proficiency
Strength
Dexterity
Constitution
Intelligence
Wisdom
Charisma
Total Hit Die
Combat
How your character looks
Hit Point Maximum
Armor Class
Initiative
Speed
Attacks
Spells
Character stats
Spellcasting Ability
Known Spells
Equipment
How your character looks
Coins
GP
SP
CP
Weapons
Armor and Shields
Miscellanious
Features
Character stats
Proficiencies
Languages
Features and Traits
Story
Fill in your credentials
Flaws
Ideals
Personality Traits
Bonds
Character Backstory


Change Email Address
Use this page to change your account email address.
Current email address
Current password
New email address


Quaternion class
class Quaternion { final Float32List storage; double get x => storage[0]; double get y => storage[1]; double get z => storage[2]; double get w => storage[3]; set x(double x) { storage[0] = x; } set y(double y) { storage[1] = y; } set z(double z) { storage[2] = z; } set w(double w) { storage[3] = w; } /// Constructs a quaternion using the raw values [x], [y], [z], and [w] Quaternion(double x, double y, double z, double w) : storage = new Float32List(4) { storage[0] = x; storage[1] = y; storage[2] = z; storage[3] = w; } /// From a rotation matrix [rotationMatrix]. Quaternion.fromRotation(Matrix3 rotationMatrix) : storage = new Float32List(4) { double trace = rotationMatrix.trace(); if (trace > 0.0) { double s = Math.sqrt(trace + 1.0); storage[3] = s * 0.5; s = 0.5 / s; storage[0] = (rotationMatrix.storage[5] - rotationMatrix.storage[7]) * s; storage[1] = (rotationMatrix.storage[6] - rotationMatrix.storage[2]) * s; storage[2] = (rotationMatrix.storage[1] - rotationMatrix.storage[3]) * s; } else { int i = rotationMatrix.storage[0] < rotationMatrix.storage[4] ? (rotationMatrix.storage[4] < rotationMatrix.storage[8] ? 2 : 1) : (rotationMatrix.storage[0] < rotationMatrix.storage[8] ? 2 : 0); int j = (i + 1) % 3; int k = (i + 2) % 3; double s = Math.sqrt(rotationMatrix.entry(i,i) - rotationMatrix.entry(j,j) - rotationMatrix.entry(k,k) + 1.0); storage[i] = s * 0.5; s = 0.5 / s; storage[3] = (rotationMatrix.entry(k,j) - rotationMatrix.entry(j,k)) * s; storage[j] = (rotationMatrix.entry(j,i) + rotationMatrix.entry(i,j)) * s; storage[k] = (rotationMatrix.entry(k,i) + rotationMatrix.entry(i,k)) * s; } } /// Rotation of [angle] around [axis]. Quaternion.axisAngle(Vector3 axis, double angle) : storage = new Float32List(4) { setAxisAngle(axis, angle); } /// Copies [original]. Quaternion.copy(Quaternion original) : storage = new Float32List(4) { storage[0] = original.storage[0]; storage[1] = original.storage[1]; storage[2] = original.storage[2]; storage[3] = original.storage[3]; } /// Random rotation. Quaternion.random(Math.Random rn) : storage = new Float32List(4) { // From: "Uniform Random Rotations", Ken Shoemake, Graphics Gems III, // pg. 124-132. double x0 = rn.nextDouble(); double r1 = Math.sqrt(1.0 - x0); double r2 = Math.sqrt(x0); double t1 = Math.PI*2.0 * rn.nextDouble(); double t2 = Math.PI*2.0 * rn.nextDouble(); double c1 = Math.cos(t1); double s1 = Math.sin(t1); double c2 = Math.cos(t2); double s2 = Math.sin(t2); storage[0] = s1 * r1; storage[1] = c1 * r1; storage[2] = s2 * r2; storage[3] = c2 * r2; } /// Constructs the identity quaternion Quaternion.identity() : storage = new Float32List(4) { storage[3] = 1.0; } /// Time derivative of [q] with angular velocity [omega]. Quaternion.dq(Quaternion q, Vector3 omega) : storage = new Float32List(4) { double qx = q.storage[0]; double qy = q.storage[1]; double qz = q.storage[2]; double qw = q.storage[3]; double ox = omega.storage[0]; double oy = omega.storage[1]; double oz = omega.storage[2]; double _x = ox * qw + oy * qz - oz * qy; double _y = oy * qw + oz * qx - ox * qz; double _z = oz * qw + ox * qy - oy * qx; double _w = -ox * qx - oy * qy - oz * qz; storage[0] = _x * 0.5; storage[1] = _y * 0.5; storage[2] = _z * 0.5; storage[3] = _w * 0.5; } /// Quaternion view of [storage]. Quaternion.view(this.storage); /// Returns a new copy of this Quaternion clone() { return new Quaternion.copy(this); } /// Copy [source] into [this] void copyFrom(Quaternion source) { storage[0] = source.storage[0]; storage[1] = source.storage[1]; storage[2] = source.storage[2]; storage[3] = source.storage[3]; } /// Copy [this] into [target]. void copyTo(Quaternion target) { target.storage[0] = storage[0]; target.storage[1] = storage[1]; target.storage[2] = storage[2]; target.storage[3] = storage[3]; } /// Set quaternion with rotation of [radians] around [axis]. void setAxisAngle(Vector3 axis, double radians) { double len = axis.length; if (len == 0.0) { return; } double halfSin = Math.sin(radians * 0.5) / len; storage[0] = axis.storage[0] * halfSin; storage[1] = axis.storage[1] * halfSin; storage[2] = axis.storage[2] * halfSin; storage[3] = Math.cos(radians * 0.5); } /// Set quaternion with rotation of [yaw], [pitch] and [roll]. void setEuler(double yaw, double pitch, double roll) { double halfYaw = yaw * 0.5; double halfPitch = pitch * 0.5; double halfRoll = roll * 0.5; double cosYaw = Math.cos(halfYaw); double sinYaw = Math.sin(halfYaw); double cosPitch = Math.cos(halfPitch); double sinPitch = Math.sin(halfPitch); double cosRoll = Math.cos(halfRoll); double sinRoll = Math.sin(halfRoll); storage[0] = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw; storage[1] = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw; storage[2] = sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw; storage[3] = cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw; } /// Normalize [this]. Quaternion normalize() { double l = length; if (l == 0.0) { return this; } l = 1.0 / l; storage[3] = storage[3] * l; storage[2] = storage[2] * l; storage[1] = storage[1] * l; storage[0] = storage[0] * l; return this; } /// Conjugate [this]. Quaternion conjugate() { storage[2] = -storage[2]; storage[1] = -storage[1]; storage[0] = -storage[0]; return this; } /// Invert [this]. Quaternion inverse() { double l = 1.0 / length2; storage[3] = storage[3] * l; storage[2] = -storage[2] * l; storage[1] = -storage[1] * l; storage[0] = -storage[0] * l; return this; } /// Normalized copy of [this]. Quaternion normalized() { return new Quaternion.copy(this).normalize(); } /// Conjugated copy of [this]. Quaternion conjugated() { return new Quaternion.copy(this).conjugate(); } /// Inverted copy of [this]. Quaternion inverted() { return new Quaternion.copy(this).inverse(); } /// Radians of rotation. double get radians => 2.0 * Math.acos(storage[3]); /// Axis of rotation. Vector3 get axis { double scale = 1.0 / (1.0 - (storage[3] * storage[3])); return new Vector3(storage[0] * scale, storage[1] * scale, storage[2] * scale); } /// Length squared. double get length2 { double _x = storage[0]; double _y = storage[1]; double _z = storage[2]; double _w = storage[3]; return (_x * _x) + (_y * _y) + (_z * _z) + (_w * _w); } /// Length. double get length { return Math.sqrt(length2); } /// Returns a copy of [v] rotated by quaternion. Vector3 rotated(Vector3 v) { Vector3 out = new Vector3.copy(v); return rotate(out); } /// Rotates [v] by [this]. Vector3 rotate(Vector3 v) { // conjugate(this) * [v,0] * this double _w = storage[3]; double _z = storage[2]; double _y = storage[1]; double _x = storage[0]; double tiw = _w; double tiz = -_z; double tiy = -_y; double tix = -_x; double tx = tiw * v.x + tix * 0.0 + tiy * v.z - tiz * v.y; double ty = tiw * v.y + tiy * 0.0 + tiz * v.x - tix * v.z; double tz = tiw * v.z + tiz * 0.0 + tix * v.y - tiy * v.x; double tw = tiw * 0.0 - tix * v.x - tiy * v.y - tiz * v.z; double result_x = tw * _x + tx * _w + ty * _z - tz * _y; double result_y = tw * _y + ty * _w + tz * _x - tx * _z; double result_z = tw * _z + tz * _w + tx * _y - ty * _x; v.storage[2] = result_z; v.storage[1] = result_y; v.storage[0] = result_x; return v; } /// Scales [this] by [scale]. Quaternion scale(double scale) { storage[3] = storage[3] * scale; storage[2] = storage[2] * scale; storage[1] = storage[1] * scale; storage[0] = storage[0] * scale; return this; } /// Scaled copy of [this]. Quaternion scaled(double scale) { Quaternion q = new Quaternion.copy(this); return q.scale(scale); } /// [this] rotated by [other]. Quaternion operator*(Quaternion other) { double _w = storage[3]; double _z = storage[2]; double _y = storage[1]; double _x = storage[0]; double ow = other.storage[3]; double oz = other.storage[2]; double oy = other.storage[1]; double ox = other.storage[0]; return new Quaternion(_w * ox + _x * ow + _y * oz - _z * oy, _w * oy + _y * ow + _z * ox - _x * oz, _w * oz + _z * ow + _x * oy - _y * ox, _w * ow - _x * ox - _y * oy - _z * oz); } /// Returns copy of [this] + [other]. Quaternion operator+(Quaternion other) { return new Quaternion(storage[0] + other.storage[0], storage[1] + other.storage[1], storage[2] + other.storage[2], storage[3] + other.storage[3]); } /// Returns copy of [this] - [other]. Quaternion operator-(Quaternion other) { return new Quaternion(storage[0] - other.storage[0], storage[1] - other.storage[1], storage[2] - other.storage[2], storage[3] - other.storage[3]); } /// Returns negated copy of [this]. Quaternion operator-() { return new Quaternion(-storage[0], -storage[1], -storage[2], -storage[3]); } double operator[](int i) => storage[i]; void operator[]=(int i, double arg) { storage[i] = arg; } /// Returns a rotation matrix containing the same rotation as [this]. Matrix3 asRotationMatrix() { double d = length2; assert(d != 0.0); double s = 2.0 / d; double _x = storage[0]; double _y = storage[1]; double _z = storage[2]; double _w = storage[3]; double xs = _x * s; double ys = _y * s; double zs = _z * s; double wx = _w * xs; double wy = _w * ys; double wz = _w * zs; double xx = _x * xs; double xy = _x * ys; double xz = _x * zs; double yy = _y * ys; double yz = _y * zs; double zz = _z * zs; return new Matrix3(1.0 - (yy + zz), xy + wz, xz - wy, // column 0 xy - wz, 1.0 - (xx + zz), yz + wx, // column 1 xz + wy, yz - wx, 1.0 - (xx + yy)); // column 2 } /// Printable string. String toString() { return '${storage[0]}, ${storage[1]}, ${storage[2]} @ ${storage[3]}'; } /// Relative error between [this] and [correct]. double relativeError(Quaternion correct) { Quaternion diff = correct - this; double norm_diff = diff.length; double correct_norm = correct.length; return norm_diff/correct_norm; } /// Absolute error between [this] and [correct]. double absoluteError(Quaternion correct) { double this_norm = length; double correct_norm = correct.length; double norm_diff = (this_norm - correct_norm).abs(); return norm_diff; } }
Constructors
new Quaternion(double x, double y, double z, double w) #
Constructs a quaternion using the raw values x, y, z, and w
Quaternion(double x, double y, double z, double w) : storage = new Float32List(4) { storage[0] = x; storage[1] = y; storage[2] = z; storage[3] = w; }
new Quaternion.axisAngle(Vector3 axis, double angle) #
Rotation of angle around axis.
Quaternion.axisAngle(Vector3 axis, double angle) : storage = new Float32List(4) { setAxisAngle(axis, angle); }
new Quaternion.copy(Quaternion original) #
Copies original.
Quaternion.copy(Quaternion original) : storage = new Float32List(4) { storage[0] = original.storage[0]; storage[1] = original.storage[1]; storage[2] = original.storage[2]; storage[3] = original.storage[3]; }
new Quaternion.dq(Quaternion q, Vector3 omega) #
Time derivative of q with angular velocity omega.
Quaternion.dq(Quaternion q, Vector3 omega) : storage = new Float32List(4) { double qx = q.storage[0]; double qy = q.storage[1]; double qz = q.storage[2]; double qw = q.storage[3]; double ox = omega.storage[0]; double oy = omega.storage[1]; double oz = omega.storage[2]; double _x = ox * qw + oy * qz - oz * qy; double _y = oy * qw + oz * qx - ox * qz; double _z = oz * qw + ox * qy - oy * qx; double _w = -ox * qx - oy * qy - oz * qz; storage[0] = _x * 0.5; storage[1] = _y * 0.5; storage[2] = _z * 0.5; storage[3] = _w * 0.5; }
new Quaternion.fromRotation(Matrix3 rotationMatrix) #
From a rotation matrix rotationMatrix.
Quaternion.fromRotation(Matrix3 rotationMatrix) : storage = new Float32List(4) { double trace = rotationMatrix.trace(); if (trace > 0.0) { double s = Math.sqrt(trace + 1.0); storage[3] = s * 0.5; s = 0.5 / s; storage[0] = (rotationMatrix.storage[5] - rotationMatrix.storage[7]) * s; storage[1] = (rotationMatrix.storage[6] - rotationMatrix.storage[2]) * s; storage[2] = (rotationMatrix.storage[1] - rotationMatrix.storage[3]) * s; } else { int i = rotationMatrix.storage[0] < rotationMatrix.storage[4] ? (rotationMatrix.storage[4] < rotationMatrix.storage[8] ? 2 : 1) : (rotationMatrix.storage[0] < rotationMatrix.storage[8] ? 2 : 0); int j = (i + 1) % 3; int k = (i + 2) % 3; double s = Math.sqrt(rotationMatrix.entry(i,i) - rotationMatrix.entry(j,j) - rotationMatrix.entry(k,k) + 1.0); storage[i] = s * 0.5; s = 0.5 / s; storage[3] = (rotationMatrix.entry(k,j) - rotationMatrix.entry(j,k)) * s; storage[j] = (rotationMatrix.entry(j,i) + rotationMatrix.entry(i,j)) * s; storage[k] = (rotationMatrix.entry(k,i) + rotationMatrix.entry(i,k)) * s; } }
new Quaternion.identity() #
Constructs the identity quaternion
Quaternion.identity() : storage = new Float32List(4) { storage[3] = 1.0; }
new Quaternion.random(Random rn) #
Random rotation.
Quaternion.random(Math.Random rn) : storage = new Float32List(4) { // From: "Uniform Random Rotations", Ken Shoemake, Graphics Gems III, // pg. 124-132. double x0 = rn.nextDouble(); double r1 = Math.sqrt(1.0 - x0); double r2 = Math.sqrt(x0); double t1 = Math.PI*2.0 * rn.nextDouble(); double t2 = Math.PI*2.0 * rn.nextDouble(); double c1 = Math.cos(t1); double s1 = Math.sin(t1); double c2 = Math.cos(t2); double s2 = Math.sin(t2); storage[0] = s1 * r1; storage[1] = c1 * r1; storage[2] = s2 * r2; storage[3] = c2 * r2; }
new Quaternion.view(Float32List storage) #
Quaternion view of storage.
Quaternion.view(this.storage);
Properties
final Vector3 axis #
Axis of rotation.
Vector3 get axis { double scale = 1.0 / (1.0 - (storage[3] * storage[3])); return new Vector3(storage[0] * scale, storage[1] * scale, storage[2] * scale); }
final double length2 #
Length squared.
double get length2 { double _x = storage[0]; double _y = storage[1]; double _z = storage[2]; double _w = storage[3]; return (_x * _x) + (_y * _y) + (_z * _z) + (_w * _w); }
final Float32List storage #
final Float32List storage
Operators
Quaternion operator +(Quaternion other) #
Returns copy of this
+
other.
Quaternion operator+(Quaternion other) { return new Quaternion(storage[0] + other.storage[0], storage[1] + other.storage[1], storage[2] + other.storage[2], storage[3] + other.storage[3]); }
Quaternion operator -() #
Returns negated copy of this
.
Quaternion operator-() { return new Quaternion(-storage[0], -storage[1], -storage[2], -storage[3]); }
Quaternion operator -(Quaternion other) #
Returns copy of this
-
other.
Quaternion operator-(Quaternion other) { return new Quaternion(storage[0] - other.storage[0], storage[1] - other.storage[1], storage[2] - other.storage[2], storage[3] - other.storage[3]); }
Quaternion operator *(Quaternion other) #
this
rotated by
other.
Quaternion operator*(Quaternion other) { double _w = storage[3]; double _z = storage[2]; double _y = storage[1]; double _x = storage[0]; double ow = other.storage[3]; double oz = other.storage[2]; double oy = other.storage[1]; double ox = other.storage[0]; return new Quaternion(_w * ox + _x * ow + _y * oz - _z * oy, _w * oy + _y * ow + _z * ox - _x * oz, _w * oz + _z * ow + _x * oy - _y * ox, _w * ow - _x * ox - _y * oy - _z * oz); }
Methods
double absoluteError(Quaternion correct) #
Absolute error between this
and
correct.
double absoluteError(Quaternion correct) { double this_norm = length; double correct_norm = correct.length; double norm_diff = (this_norm - correct_norm).abs(); return norm_diff; }
Matrix3 asRotationMatrix() #
Returns a rotation matrix containing the same rotation as this
.
Matrix3 asRotationMatrix() { double d = length2; assert(d != 0.0); double s = 2.0 / d; double _x = storage[0]; double _y = storage[1]; double _z = storage[2]; double _w = storage[3]; double xs = _x * s; double ys = _y * s; double zs = _z * s; double wx = _w * xs; double wy = _w * ys; double wz = _w * zs; double xx = _x * xs; double xy = _x * ys; double xz = _x * zs; double yy = _y * ys; double yz = _y * zs; double zz = _z * zs; return new Matrix3(1.0 - (yy + zz), xy + wz, xz - wy, // column 0 xy - wz, 1.0 - (xx + zz), yz + wx, // column 1 xz + wy, yz - wx, 1.0 - (xx + yy)); // column 2 }
Quaternion clone() #
Returns a new copy of this
Quaternion clone() { return new Quaternion.copy(this); }
Quaternion conjugate() #
Conjugate this
.
Quaternion conjugate() { storage[2] = -storage[2]; storage[1] = -storage[1]; storage[0] = -storage[0]; return this; }
Quaternion conjugated() #
Conjugated copy of this
.
Quaternion conjugated() { return new Quaternion.copy(this).conjugate(); }
void copyFrom(Quaternion source) #
Copy
source into this
void copyFrom(Quaternion source) { storage[0] = source.storage[0]; storage[1] = source.storage[1]; storage[2] = source.storage[2]; storage[3] = source.storage[3]; }
void copyTo(Quaternion target) #
Copy this
into
target.
void copyTo(Quaternion target) { target.storage[0] = storage[0]; target.storage[1] = storage[1]; target.storage[2] = storage[2]; target.storage[3] = storage[3]; }
Quaternion inverse() #
Invert this
.
Quaternion inverse() { double l = 1.0 / length2; storage[3] = storage[3] * l; storage[2] = -storage[2] * l; storage[1] = -storage[1] * l; storage[0] = -storage[0] * l; return this; }
Quaternion inverted() #
Inverted copy of this
.
Quaternion inverted() { return new Quaternion.copy(this).inverse(); }
Quaternion normalize() #
Normalize this
.
Quaternion normalize() { double l = length; if (l == 0.0) { return this; } l = 1.0 / l; storage[3] = storage[3] * l; storage[2] = storage[2] * l; storage[1] = storage[1] * l; storage[0] = storage[0] * l; return this; }
Quaternion normalized() #
Normalized copy of this
.
Quaternion normalized() { return new Quaternion.copy(this).normalize(); }
double relativeError(Quaternion correct) #
Relative error between this
and
correct.
double relativeError(Quaternion correct) { Quaternion diff = correct - this; double norm_diff = diff.length; double correct_norm = correct.length; return norm_diff/correct_norm; }
Vector3 rotate(Vector3 v) #
Rotates
v by this
.
Vector3 rotate(Vector3 v) { // conjugate(this) * [v,0] * this double _w = storage[3]; double _z = storage[2]; double _y = storage[1]; double _x = storage[0]; double tiw = _w; double tiz = -_z; double tiy = -_y; double tix = -_x; double tx = tiw * v.x + tix * 0.0 + tiy * v.z - tiz * v.y; double ty = tiw * v.y + tiy * 0.0 + tiz * v.x - tix * v.z; double tz = tiw * v.z + tiz * 0.0 + tix * v.y - tiy * v.x; double tw = tiw * 0.0 - tix * v.x - tiy * v.y - tiz * v.z; double result_x = tw * _x + tx * _w + ty * _z - tz * _y; double result_y = tw * _y + ty * _w + tz * _x - tx * _z; double result_z = tw * _z + tz * _w + tx * _y - ty * _x; v.storage[2] = result_z; v.storage[1] = result_y; v.storage[0] = result_x; return v; }
Vector3 rotated(Vector3 v) #
Returns a copy of v rotated by quaternion.
Vector3 rotated(Vector3 v) { Vector3 out = new Vector3.copy(v); return rotate(out); }
Quaternion scale(double scale) #
Scales this
by
scale.
Quaternion scale(double scale) { storage[3] = storage[3] * scale; storage[2] = storage[2] * scale; storage[1] = storage[1] * scale; storage[0] = storage[0] * scale; return this; }
Quaternion scaled(double scale) #
Scaled copy of this
.
Quaternion scaled(double scale) { Quaternion q = new Quaternion.copy(this); return q.scale(scale); }
void setAxisAngle(Vector3 axis, double radians) #
Set quaternion with rotation of radians around axis.
void setAxisAngle(Vector3 axis, double radians) { double len = axis.length; if (len == 0.0) { return; } double halfSin = Math.sin(radians * 0.5) / len; storage[0] = axis.storage[0] * halfSin; storage[1] = axis.storage[1] * halfSin; storage[2] = axis.storage[2] * halfSin; storage[3] = Math.cos(radians * 0.5); }
void setEuler(double yaw, double pitch, double roll) #
Set quaternion with rotation of yaw, pitch and roll.
void setEuler(double yaw, double pitch, double roll) { double halfYaw = yaw * 0.5; double halfPitch = pitch * 0.5; double halfRoll = roll * 0.5; double cosYaw = Math.cos(halfYaw); double sinYaw = Math.sin(halfYaw); double cosPitch = Math.cos(halfPitch); double sinPitch = Math.sin(halfPitch); double cosRoll = Math.cos(halfRoll); double sinRoll = Math.sin(halfRoll); storage[0] = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw; storage[1] = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw; storage[2] = sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw; storage[3] = cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw; }


by
Robin Johnson1
Statistics New Zealand adopted the Australian and New Zealand Standard Industrial Classification in 1993 replacing the NZSIC previously in vogue. A number of key sectors of the economy are affected by the changes in definition in a way that makes all new data sets lack compatibility with those that went before. This is particularly the case with the Philpott collection where the industrial classification was all NZSIC. Since 1993 Real GDP, Labour Employment and Gross Capital Investment have all been on a ANZSIC basis. This paper discusses the problems that were created by the somewhat arbitrary changes that were made and suggests a methodology of overcoming the matching problem. Research and Development statistics collected by MoRST are also classified by the NZSIC system. The paper concludes with a discussion of the standardisation of Research and Development statistics to the different industrial classifications.
Economists are heavily reliant on the statistical data sources which describe the economy. There is an inherent trust that the materials have been put together in an objective and scientific manner. In the industrial classification area, the main disaggregation takes place for industry by industry analysis. Furthermore the continuing series of statistics provides the time series for most of the econometric testing of economic models. The Philpott series of data sets is a good illustration of productive use that many of these industry by industry collections have been put. The problem with the Australian and New Zealand Standard Industrial Classification (ANZSIC) is that it puts a discontinuity in the time series of all statistical collections that rely on the industry classification.
ANZSIC was producd by the Australian Bureau of Statistics and the New Zealand Department of Statistics for use in the collection and publication of statistics in the two countries. The development of the ANZSIC resulted from the need to improve comparability of industry statistics for the two countries. International comparability was enhanced by aligning the ANZSIC with the International Standard Industrial Classification of All Economic Activities (ISIC), Revision 3, where possible. Users of industry statistics in both countries have been widely consulted in the development of the ANZSIC. In preparing the classification considerable effort has gone into reaching a suitable balance between maintaining historic comparability and keeping pace with changes in technology and in the business environment (Preface to the ANZSIC 1993 classification).
This paper is about maintaining historic comparability in the output, capital and labour employment series on an industry basis. The series actually reviewed are the Real Gross Domestic Product estimates on a 1995-96 price base, Full and Part Time Employment, and Gross Fixed Capital Formation. The capital stocks data employed by Prof Philpott was derived by him from the NZSIC gross capital formation series and hence is not an official series (though one has since been published by Statistics New Zealand). We therefore examine the two sets available and see where they can be amalgamated or modified. We produce a new set of real GDP data back to 1960 compatible with ANZSIC. We examine full time employment statistics and their availability and we construct compatible capital formation and capital stock data for the period 1987-2000. We end with a brief discussion of industry classification in the MoRST survey of Research and Development expenditure since 1989.
The 1995-96 revision of the Statistics Real GDP Series involved not only changes to the base year prices, but the introduction of chain-linking price valuations and a change in the industrial classification of industries employed (ANZSIC) using SNA93 methodology. All these changes make linking the new estimates to old estimates produced by Statistics extremely difficult on a consistent basis. The well-known Philpott tables (1994, 1999) were based on the Statistics data using the old classification system (NZSIC) for sectors and on the UNSNA system dated 1968. Statistics produced several years of the accounts for overlapping years (1986-96) and according to Haugh (2001) were going to extend the new series back to years previous to 1986.
In this section I try to identify where the most important changes took place and construct a model for revising the Philpott series to be consistent with the 1995-96 price-based ANZSIC revision. I first assess the recalibration work done by Haugh (2001) at Treasury.
Statistics New Zealand (SNZ) released upgraded national accounts at the end of 2000 and in mid 2001. These introduced a number of important changes, including moving from a fixed weight to a chain linked calculation of constant price data and the introduction of SNA2 93 and ANZSIC definitions and classifications. In what follows, these series are referred to as fixed and chain series respectively.
The new SNA 93 ANZSIC chain series are New Zealand's official data series and replace the previous official series, which was based on SNA 68 NZSIC fixed weights data. The new chain series for production sectors is available from the June quarter of 1987 onwards. The fixed series for production sectors is available from September 1977 and the fixed expenditure series from June 1982. SNZ intend to eventually back date the chain series to the same beginning dates as the fixed series but no deadline for this has been set.
On the production side3, the type and level of disaggregation of the chain and fixed series are very similar to that previously provided; i.e. 27 (fixed) and 28 (chain) industry categories including categories such as agriculture, fishing, forestry, etc. The chain series has a greater level of disaggregation than the fixed series with finance and insurance now split out from real estate and business services.
A search for annual GDP data and non-official data also revealed a number of potentially useful series. These include the Philpott (1994, 1999) annual production GDP series that runs from 1960 to 1998 and the Reserve Bank of New Zealand's (RBNZ) (2001) quarterly expenditure GDP that begins in 1965.
However, in this paper we focus on quarterly data from 1977 for several reasons. Quarterly data enable a more precise dating of the timing of any changes in the characteristics of growth and cycles that may have occurred. Furthermore, we want to minimise the chances that changes in data construction methods will obscure any real change in the underlying series. Data available from 1977:3 have been consistently derived on an SNA basis.
At a broad level, the industry classifications of the fixed and chain series are similar, but there are some differences. Therefore, in order to create as comparable fixed and chain series as possible from an industry classification perspective, a number of transformations were made.
The level of aggregation and classification across the fixed and chain datasets differs in some cases. Finance, insurance, business services and real estate is one category in the fixed data but is split into Finance and insurance and Real estate and business services in the chain series. Government and community services, have different classifications in the chain and fixed series. For example, in the fixed series Community services were classified by provider, i.e. private, central government and local government. In the chain series they are classified by industry, regardless of whether the service provider is market or non-market. In order to create comparable categories some industries have been combined into larger categories.
For example, the chain Finance, insurance and business services and real estate series have been summed to create a combined category for comparability with the fixed series, which is only available at this higher level of aggregation.
In some cases similarly labelled series, e.g. Other manufacturing, are measuring different things in the fixed and chain series because they are combinations of different industries under ANZSIC compared to NZSIC. Furniture manufacturing has moved from Wood products manufacturing to Other manufacturing. In three cases, Other manufacturing, Wood products manufacturing and Basic metals manufacturing, a conversion concordance from NZSIC to ANZSIC was used to create fixed ANZSIC series for comparison with the chain ANZSIC series. For example, 75% of the old NZSIC Wood products manufacturing industry and 50% of the old NZSIC Pulp and paper industry have shifted to the new ANZSIC Wood and paper products manufacturing category. A very approximate fixed weight Wood and paper products manufacturing ANZSIC series was created by summing 50% of the NZSIC Pulp and paper industry and 75% of the NZSIC Wood products manufacturing industry. These derived fixed weight ANZSIC series have higher correlations with their chain ANZSIC series counterparts than the original fixed NZSIC series. For example, for Wood and paper products manufacturing the correlation for first differences of the logs of the series rises from 0.55 to 0.88.
Wholesale trade Retail trade and Accommodation & restaurants data are available on a similar basis in both data sets but the sub-industries Retail trade and Accommodation & restaurants are only available from June 1986 so these have been combined with Wholesale trade to make a combined category comparable with wholesale trade from September 1977 to March 1986.
The calibration method uses the relationship between the growth rates (more specifically the first differences of the logs) of the seasonally adjusted chain and fixed volume [quarterly] series. The growth rates as opposed to the levels are used because the growth rates appear to be stationary while the levels are not. Visual inspection of the growth rate data indicates that in particular they fluctuate around a long run constant mean with stable variance although they may move from local mean to local mean within the business cycle. The Phillips-Peron test also indicated that all the growth series (both fixed and chain) were stationary apart from owner occupied dwellings for both the fixed and chain series, which show some short-term trending behaviour.
The chain and fixed series growth rates were compared by industry in two ways. The first method was to compare the mean and standard deviation of the quarterly growth rates for the period June 1987 to June 2000 for which there was common data. The second method of comparison was to construct both scatter and time series plots of the growth rates of the chain and fixed series by industry to examine how well the two series were related and how time affected the relationship if at all.
Figure 1 shows the difference between the mean quarterly growth rates for the chain and fixed series by industry. Figure 2 shows the difference between the standard deviation of the quarterly growth rate for the chain and fixed series by industry over the period 1987 to 2000.
Figure 1: Chain minus Fixed Mean Quarterly Growth Rates 1987 to 2000
Figure 2: Chain minus Fixed standard deviation for the period 1987 to 2000
The comparison of mean growth rates shown in Figure 1 illustrates that the mean growth rates for total production GDP are very similar. The mean growth rate for the chain series is only 0.04 percentage points higher than the fixed series. By industry, the range is from 0.6 percentage points for Other manufacturing to 0.4 percentage points for Wood and paper manufacturing (chain- fixed).
As noted above the Other manufacturing and Wood product manufacturing series have been affected by new industry definitions, which have resulted in Furniture manufacturing moving from Wood and paper product manufacturing to Other manufacturing, and Pulp and paper moving from Printing, publishing, pulp and paper to Wood and paper manufacturing. For 23 out of the 29 industries including the 5 main sectors, the quarterly growth means for the chain and fixed weight series are within +/-0.2 percentage points of each other.
Using visual examination of the time series plots, the industries and total GDP were divided into 3 broad categories: (1) chain and fixed series that are noticeably different over the whole period (September 1987 to June 2000); (2) chain and fixed series that are very similar over the first half of the period but the relationship is less clear in the second half; and (3) series where the relationship between the chain and fixed series is very similar for the whole period.
There are only two small industries in category (1), Mining and other manufacturing. The upgrade of the national accounts has led to significant changes in these series and even after correcting for ANZSIC changes in Other manufacturing the relationship between the 2 series is more diffuse than for other industries. The calibrated results for these two industries should therefore be treated with more caution than for other industries.
The industries in category (2) might contain a structural break in the relationship between the chain and the fixed series because in some cases SNZ have used a method for calculating the series from the June 1994 quarter that differs from the method used prior to this date. For example, sub-component volume indexes for some industries, e.g. meat and dairy volume indexes for the Primary food manufacturing industry have been calculated using updated 1996 commodity weights but the new weights have only been used from June 1994 onwards. In other manufacturing industries, ANZSIC producer prices have been used in deflation from June 1994, but weighted NZSIC producer prices were used prior to this date. Any changes in the methodology affecting the price indexes used in deflation could flow through to the constant price series. In other cases for example, Wholesale trade, Retail trade and Accommodation, cafes and restaurants calculations are performed with a finer level of detail than previously but the changes have only been backdated to June 1994. Prior to 1994 these series are linked to weighted proportions of old NZSIC sub-component series.
At the 5% level of significance the null hypothesis of no structural break was only rejected for Combined wholesale trade; Electricity, gas and water; Forestry; Owner occupied dwellings; and Other food manufacturing. For the first two industries there is also clear visual evidence of a structural break, with the relationship becoming more diffuse in the second half of the data.
Statistics New Zealand have calculated these industries with different weights and/or a finer level of detail than previously but only made these changes to the series back to June 1994.
As these changes will not be made to the series prior to June 1987 either, the relationship between the fixed and chain series for these two industries is best captured by estimating the relationship for the period June 1987 to March 1994 only.
Haugh offers correction factors for the Wood, Paper, Printing, & Other Manufacturing sectors based on splits/concordances (derived I presume from Stats before and after [quarterly?} tables) as to how they were classified before and after SNA 93 and ANZSIC. He also has a split for Basic Metals and Machinery. I have applied these to the Philpott series in 1987-88 and produced plausible substitutes that are price consistent with the overall change in prices between the data based on 1982-83 prices and that based on 1995-96 prices. Wholesale Trade, and Finance and Insurance, are amalgamated by Haugh and thus satisfy his tests. As Philpott used this aggregation in his series (and has not recorded the individual components) this overcomes any problems for our conversion. Finally, Government Services and Personal Services show unusual price changes between the two bases which suggest the content or the methodology has been changed between the two revisions (discussed below).
I propose to use the Haugh conversion factors where they apply and check these against the before and after annual estimates for 1987-88 at 1995-96 prices. Statistics (Nick Treadgold) inform me that for Communications and O-O Dwellings there are no changes between ANZSIC and NZSIC definitions. Small changes made were due to the re-estimation of the consumption of fixed capital and the capitalisation of computer software in the former.
For Government, there were significant differences.between ANZSIC and NZSIC. While Local Government was unchanged, education and health components of Central Government were moved into Personal Services. Personal Services also have a number of other changes which increase the differences slightly.
From these comparisions I have constructed the following adjustments to the Philpott industry categories in 1987-88 as in Box 1.
Box 1: Concordance Factors for Real GDP in 1987-88
I. Assume 30% of Wood and Wood Products is Furniture (rest combines with balance of Paper Products to form Wood and Paper Product Manufacturing).
II. Assume 54% of Paper Products, Printing & Publishing is Printing and Publishing (balance to Wood and Paper Product Manufacturing).
III. Asume 30% of Machinery & Metal Products is Fabricated Metal Products and add to Basic Metal Product Manufacturing (balance is Machinery and Equipment).
IV Add Furniture to Other
V. Maintain Finance, Insurance, Property and Business Services as an aggregate
VI. Assume Government (Central and Local) is 34% of combined total of Government and Personal Services (the ratio in 1987-88).
VII. All other industries assumed to be defined approximately the same in both classifications.
These are percentage ratios and can be applied to Philpott's data still expressed in 1982-83 prices. Philpott's industry classification now becomes ANZSIC based.
This all assumes that these concordance ratios hold constant back thru time. We want to go back to 1960. By taking 1987-88 proportions we preserve any trend shown in the Philpott data but the absolute amounts would be increasingly distorted for the industries concerned the further we go back. As an alternative I could suggest that we simply weld the Philpott data to the ANZSIC data base (as I have done previously) by matching the old data to the new data at a common year. This would preserve the trend in the old data but the absolutes would still have less meaning compared with the ANZSIC-adjusted framework.
Finally we need to think about the base year prices of the restored series. It would be convenient to bring the past data into 1995-96 prices as far as the present chain-linked series is concerned; but how long will Statistics stay with this price base? For some purposes, it will be necessary to continue the old series at 1982-83 prices. I have re-worked the implicit price comparisions between the 82-83 base and the 95-96 base by industries when the above corrections are taken into account. A full chain linked series back to 1960 will have to wait until the Statistics revisions are finished.
File 87/88 comparisions GDP contains:
Up to 1990, Philpott (1994) based his estimates on the Quarterly Employment Survey. Agriculture was estimated from the Agricultural Census. In Philpott (1999), he based his estimates on the Household Labour Force Survey (HLFS). The data are March year annual averages of the quarterly HLFS statistics with full time equivalents calculated as one part time worker = 0.35 a full time worker (RPEP No 293). This data was welded to the earlier QES data from 1987.
I have continued this series up to March year 2002 by asking Statistics to recalculate the full- time and part-time employment by industry on the same basis. The new data is welded to the old data from1997-98. This set is only NZSIC compatible. (See file "philpott base xls")
The industry classification used by Philpott and Statistics is shown in Box 2.
Box 2: Employment by Industry: NZSIC and HLFS classification(Philpott)
According to Statistics ( J Morris) current labour employment statistics are ANZSIC based back to 1996. Part-time workers are weighted at 0.5 FTEs. FTEs from the QES are available at the ANZSIC industry 1 level (excluding agriculture) from 1989 to the present. Up to June 2003, there was a transition period in the conversion from NZSIC to ANZSIC while both codes were used. The March 2003 quarter of the HLFS featured a change to the industry coding system [on the returned questionaires] on an ANZSIC basis at the 3 digit ANZSIC level. This was to overcome certain non-sampling error problems like identifying the correct industry a firm belonged to and other possible processing errors. NZSIC-based statistics have been discontinued.
Thus Philpott's series were never compatible with the published weighted series though they were compatible with the QES data itself. Statistics has ongoing research to take the ANZSIC series back to 1970.(J Morris). In the meantime the only long term series is that of Philpott which is not compatible with ANZSIC-based real GDP. I have not investigated a concordance system that might align the NZSIC based data to ANZSIC.
I will discuss the Philpott approach first and then the approach adopted later by Statstics New Zealand. We start with capital investment in each industry and then proceed to estimating stocks of capital. In later work, Philpott (1994, 1999) used the GCF values given in the Expenditure estimates of GDP in the aggregate and industry estimates from NZIER. as Statistics had not generated the data for industry GCF after 1990 at that time. Statistics has now produced a new GCF series from 1972 to 1997 (recently up-dated to 1999) on the ANZIND classification. ANZIND is is an ANZSIC based classification used to group industries for publication ( Nick Treadgold). Philpott's data is all NZSIC based.
For deflating the nominal series of GCF, Philpott used the implicit deflator for GCF from the aggregate expenditure GDP series. This applied across all industries. In updating Philpott's stock series to 1999 I have used the All Groups Capital Goods Price index which has been available since December 1989 and the Stats GCF data from 1990-91. The same price index is used across all industries. Philpott appears to have been slightly under-estimating the absolute level of nominal CGF though not of real GCF. e.g.
| Total GCF Expenditure-Nominals $m | ||||||||
|---|---|---|---|---|---|---|---|---|
| Years | 90-1 | 91-2 | 92-3 | 93-4 | 94-5 | 95-6 | 96-7 | 97-8 |
| Stats | 14421 | 11972 | 12571 | 15035 | 17747 | 19890 | 21110 | 20678 |
| Philpott | 13948 | 11702 | 12612 | 14870 | 17212 | 18651 | 19379 | - |
| Real GCF $m | ||||||||
| New estimate | 9986 | 8091 | 8286 | 9684 | 11242 | 12441 | 13195 | - |
| Old Estimate | 9658 | 8161 | 8817 | 10405 | 11348 | 12261 | 12971 | - |
While the Stats GCF data is ANZSIC consistent, I have not adjusted it to an NZSIC basis in these estimates. Where the largest discrepancies occur (in the same industries as for real GDP) the percentage changes in the ANZSIC series have been married to Philpott's NZSIC data sets.
Net Capital Stocks refer to an perpetual inventory (PIM) series of Philpott's which assumes wastage of assets is equal to the IRD rates of depreciation for land and buildings, and plant and equipment. Starting values are derived from balance sheet data for 1950 (RPEP Paper 270). Philpott derived stock values for buildings and plant separately and then added them. Each year deflated capital investment is added and depreciation deducted. The end-of-year values become the starting values for the following year.
The new Statistics series on capital formation does not give buildings
and plant separately so my updating averages the depreciation rates and
applies the PIM model to the aggregates (This information can be obtained
from Stats if needed). To obtain average depreciation over both types of
asset the actual wastage
in 1990-91 in each sector was estimated from
Philpott's data for that year and applied to subsequent years. Over all groups,
the average wastage in that year was 6.25%. This could vary slightly from year
to year as the industry weightings change.
| All Groups Total NZSIC (at beginning of year shown) | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Years $m | 88-9 | 89-0 | 90-1 | 91-2 | 92-3 | 93-4 | 94-5 | 95-6 | 96-7 | 97-8 |
| New estimates | 122817 | 124878 | 127412 | 129434 | 129436 | 129632 | 131215 | 13425 | 138276 | - |
| Old estimates | 122376 | 124033 | 126628 | 128562 | 128886 | 129912 | 132464 | 136431 | 140470 | 144988 |
Gross Capital Stocks refer to stocks where wastage is estimated by a time of
retirement methodology. Basically assets stay in use until they are discarded.
This methodology requires estimates of the life
of an asset (Philpott calls them service lives). He says: ‘Consistent with the OECD approach we assumed that assets began to be retired at a point in time 20%
earlier than their average service life and that this retirement concluded at a
point 20% later than that date and that the retirement process was
linear’. For an asset of life 20 years,
the wastage would be spread as one eighth from year 16 to year 24 (RPEP Paper
No 105).
Starting values were estimated for 1950 and a profile of retirement rates estimated for that year and spread over subsequent years. All new investment was then retired by the rules outlined above.
For up-dating purposes we do not have these time profiles of the Philpott gross stock estimates. It is possible, however, to estimate what the end-effect is in any one year by deducting capital investment from the change in gross stocks. This varies from industry to industry (with different service lives assumed) and in 1990-91 the national retirement of assets was 2.003% of the stock at the beginning of the year. Transport was as high as 5.46% and Electricity was 1.22% per year.
It follows that this percentage will not be constant but will vary according to the time profile of capital investment 20-40 years earlier. Here is a comparison for some industries for 1996-97 compared with 1990-91:
| Implicit Wastage | ||||
|---|---|---|---|---|
| Industry | 1990-91 | 1996-97 | ||
| Total | % of Stocks | Total | % of Stocks | |
| Agriculture | $557m | 2.902% | $499 | 2.525 |
| Electricity & Water | $227 | 1.223 | $235 | 1.163 |
| Finance and Insurance | $269 | 1.288 | $302 | 1.039 |
| All Groups | $4378 | 2.003 | $4640 | 1.837 |
Apparently, the amount being discarded is getting larger except in the case of agriculture. This reflects higher investment in some earlier period. While the total amount being discarded is higher it is lower in percentage terms as the stock has grown larger in the interim.
For my updating I have assumed that the 1990-91 depreciation/wastage rates for each industry category held throughout the 1990s.
| All Groups NZSIC (at beginning of year shown) | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Years $m | 88-9 | 89-0 | 90-1 | 91-2 | 92-3 | 93-4 | 94-5 | 95-6 | 96-7 | 97-8 |
| New estimates | 210323 | 215847 | 221863 | 227404 | 230941 | 234601 | 239586 | 246029 | 253513 | - |
| Old estimates | 208466 | 212655 | 218584 | 223864 | 227639 | 231986 | 237932 | 244809 | 252552 | 260883 |
Statistics prepared their own capital stock series in 1999. To do this they updated the GCF data from 1990 on an ANZIND basis. They have published a current price series of capital stocks on an industry basis back to 1972 and a 1995-96 price chain linked industry series back to 1972. These estimates of stocks use the same GCF series as used above for the Philpott corrections, but the base stocks and depreciation rates are defined differently.
In Statistics (2000) gross capital stock is defined as accumulated investment less the
accumulated value of the assets no longer operating (that is, retired)
. Net
capital stock represents accumulated investment less retirements
and accumulated depreciation for assets still operating (that is, gross capital
stock less accumulated depreciation on assets still in operation).
Depreciation figures are based on assets valued at current replacement cost
and consistent rates of depreciation which better approximate the useful lives
of assets.
In summary:
Opening balance sheet value of a specific asset, plus
Value of assets aquired, less
Value of assets disposed of or depreciated, plus
Value of other positive or negative changes (war destruction), plus
Value of the positive or negative holding gains accruing during the period such as cost of
replacement, equals
Value of stock of asset in closing balance sheet.
Statistics (Pipat Wongsaart) say that, as a result of market forces, the purchaser's price of a new fixed asset should provide a good initial estimate of the present value of the future rentals. This applies to all years throughout the period covered. Hence, net capital stock for each of these years can be defined as the total values of fixed assets in a particular year taking into account the asset's efficiency loss and cost of capital. I have not sighted a table of service lives or how it is treated in individual industries.
Thus these estimates differ from Philpott in the way they take account of disposal of assets and in the way they take account of extraneous events. I am not clear how the service life definition differs. I remember Brian Easton has raised this issue of lost assets previously in discussion of the Philpott data.
The chart (next page) shows Philpott's two real series as compared with the new Stats real series all in 1995-96 prices. Philpott's net stocks are growing more slowly than the Stats estimate due to higher depreciation (IRD rates). In 1999-2000 Stats are 46% higher than Philpott net stocks. On the other hand, Philpott's gross stock series are 24% higher than Stats net stocks (as might be expected) in 1999-2000 due to the lower wastage through his assumption of service lives and his lack of data on asset disposal. If the increment of investment is common to Stats and Philpott (as adjusted by myself), the difference in growth must be due to depreciation/wastage/write-off rates
Can Stats series be used for econometric work? They are ANZSIC consistent and hence match the revised real GDP data base for industry from 1972. Social capital definitions need to be looked at. Only the current employment data is not ANZSIC consistent before 1989. So Ralph can do production functions on a consistent basis from 1989 to the present using Stats data. This is a lot of work to get such a simple answer, Ralph!
Stats has seriously disturbed the historical collection of macroeconomic statistics in NZ by introducing ANZSIC. They say they had some regard for continuity of previous statistical collections. It seems to me that the authors should have had a plan to convert GDP, employment and GCF on a similar basis for similar time periods back to some common date in the past. At the moment it would be a good idea if someone extended back the employment statistics on an ANZIND basis back to 1972 as a matter of priority (I am assured [by J Morris] that a team is working on it).
Haugh David, (2001), Calibration of a Chain linked Volume Production GDP Database, NZ Treasury Web site.
MoRST (Var), NZ Research and Experimental Development Statistics: All Sectors, Ministry of Research, Science and Technology.
Philpott, Bryan (1994), Data Base of Nominal and Real Output, Labour, and Capital Employed by SNA Industry Group, Research Project on Economic Planning Paper 265, School of Economics and Finance, Victoria University of Wellington.
Philpott, Bryan (1999), Provisional Estimates for 1990-1998 of Output Labour & Capital Employed by SNA Industry Group, Research Project on Economic Planning Paper 293, School of Economics and Finance, Victoria University of Wellington.
Statistics NZ (2000), Measuring Capital Stock in the New Zealand Economy, Key Statistics, December.
(i) Research and Development Expenditure
Since 1989, MoRST have carried out annual or semi-annual surveys of Research and Development expenditure in New Zealand (MoRST var). These carefully delineate research expenditure in the major providers of research; government, firms and universities, and also identify which productive sectors the research is aimed at. The surveys also carefully differentiate between fundingfunctions and provider functions. Thus for the period 1989-90 to 2000-01 there is a detailed record of research expenditure on a provider (who carries it out) and a funder basis (who pays for it) including the designated sectors to which the research is directed. For the analysis of impacts of this research endeavour, the provider basis of Research and Development is the best guide.
For the period from 1989 back to 1962, the record of Government expenditure on Research and Development is almost complete. Total departmental funding is faithfully recorded in the Department of Statistics' Yearbooks and by NRAC and designated industries affected by areas of research are identified on a broad basis. Some extrapolation of data is required to get sectoral public expenditure back to 1962 on a consistent basis.
(ii) NZSIC Groups
In combination with the productive sectors recognised in the MoRST surveys, the Yearbook coverage determined the number of sectors which could be analysed for the whole period back to 1960 (Research and Development sector below). As the productivity data is presented on a national accounting basis (SNA) according to NZSIC, the following schema shows the sectoral allocation used in my research papers:
| MoRST output classes | Research and Development sector | NZSIC sectors (Philpott) |
| Animal industries
Dairy industries Forage Horticultural, Arable |
Agriculture | Agriculture |
| Fisheries
Forest products Food & beverages |
Fishing
Forestry Processing |
Fishing Forestry Food, Wood, Paper, Textiles |
| Materials
Engineering Electronics |
Manufacturing | Mining, Basic Metal,Chemicals, Non-metallics, Machinery |
| Energy
Construction Transport services Tourism |
Energy
Building Transport Services |
Electricity, Gas and Water
Building and Construction Transport and Storage Services Trade, Communications, Finance, Community Services |
| Information
Planning Social sciences Environment Fundamental Health Defence |
||
| Total Market Sectors
Ownership of Occupied Dwellings ditto Government Total, All Production Groups |
Production sector
ditto ditto All sectors |
(File reference is Research and Development Base.xls
)
Appendix 2: Letter from Statistics New Zealand
12 May 2004
In summary, we would not agree that Statistics New Zealand have seriously disturbed the historical economic series. or have the changes in ANZSIC been arbitarily made:
The adoption of ANZSIC was introduced to align the industrial classification used by Statistics New Zealand with other international agencies.Industrial classifications need to be updated perioically to reflect changes in technology and the business environment.
Users are consulted throughout the process of classification revisions.
The range of industrial classifications will be wider when backdating has been completed. In particular:
The range of capital series has been extended significantly since ANZSIC was introduced. Official capital expenditure series by NZSIC industry were previously only available between 1972 and 1987. Since the shift to ANZSIC, these series (both nominal and real) have been backdated to 1972 and are updated on an annual basis (usually t-3).
In addition to capital expenditure series, productivity capital stock, net capital stock, and consumption of fixed capital (replacement cost) by industry have been compiled from 1972 to t-3. None of these series were not available under NZSIC.
The differences between the capital stock series produced by Philpott and Statistics New Zealand are due to some significant differences in assumptions and methodologies. This would appear to fall outside the purpose of this paper.
Statistics New Zealand's capital stock series have been compiled consistently with the
methodologies outlined in the OECD Manual Measuring Capital Stock
. The main
pointof difference is the use of rental prices to weight together assets. This
is not currently done in the StatisticsNew Zealand model.
A couple of points of clarification:
The mining industry has been significantly affected by conceptual changes introduced with SNA93. Where previously mineral exploration was treated as a current expense it is now capitalised and included in gross fixed capital formation
The methodology for compiling primary food manufacturing has not changed between pre-1994 and post-1994. The series have simply been rebased. 1995-96 is the expression period, not the base period. Under chain-linking, the base period is effectively updated on an annual basis.
Note that the ANZIND classification is also that used to publish the output (real GDP) and capital series. It is also that which will be used for the backdated employment series.
Splits of buildings and plant gross fixed capital formation (both nominal and real)are readily available in Statistics New Zealand's published output.
The HLFS is only ANZSIC based back to 1996. This because the HLFS does not code industry low enough to allow a full concordance to be used . Similarly the period up to March (not June as stated) 2003 with dual coding also refers to the HLFS.
The QES is available on ANZSIC back to 1989.
[1] Consulting Economist, Wellington. Grant Scobie and Ralph Lattimore first suggested this project. Considerable help has been received from Statistics NZ which is acknowledged in the text.
[2] United Nations System of National Accounts
[3] Statistics New Zealand also recently released chain-linked expenditure based estimates of quarterly real GDP disaggregated by the usual major expenditure classifications: private consumption, investment, government expenditure, exports and imports. Some of these components are further disaggregated into sub-components.