/*global require*/
// dependencies
var _ = require('./core.utils');
var array1d = _.array1d;
var check = _.check;
var isObject = check.object;
var isArray = check.array;
var array2d = _.array2d;
var fens = require('./fens');
var FeNodeSet = fens.FeNodeSet;
var gcells = require('./gcellset');
var P1 = gcells.P1;
var L2 = gcells.L2;
var Q4 = gcells.Q4;
var H8 = gcells.H8;
/**
* @module mesh
*/
/**
* @typedef module:mesh.MeshInitOption
* @property {FeNodeSet} fens - optional. finite element node set.
* @property {Array} xyz - optional. 2D array of node coordinates.
* @property {GCellSet} gcells - optional. geometry cell set.
* @property {String} gcellsType - optional. finite element node set.
* @property {Array} conn - optional. connectiviy list.
*/
/**
* Mesh class.
* @class
* @param {module:mesh.MeshInitOption} options
* @example
var msh1 = new Mesh({
fens: new FeNodeSet(...),
gcells: new L2(...)
});
var msh2 = new Mesh({
xyz: [...],
gcellsType: 'L2',
conn: [...]
});
*
*/
exports.Mesh = function Mesh(options) {
if (!isObject(options)) options = {};
if (options.xyz) options.fens = new FeNodeSet({ xyz: options.xyz });
this._fens = options.fens;
this._gcells = options.gcells;
};
var Mesh = exports.Mesh;
/**
* Returns finite element node set of the mesh.
* @returns {module:fens.FeNodeSet}
*/
exports.Mesh.prototype.fens = function() { return this._fens; };
/**
* Returns geometry cell set of the mesh.
* @returns {module:gcellset.GCellSet}
*/
exports.Mesh.prototype.gcells = function() { return this._gcells; };
/**
* @callback module:mesh.MapCallback
* @param {module:types.Vector} coords
* @param {Int} i
* @returns {module:types.Vector}
*/
/**
*
* Apply the mapping function the each vertex, return the new mesh.
* @param {module:mesh.MapCallback} mapping - the mapping function.
* @returns {module:mesh.Mesh}
*/
exports.Mesh.prototype.map = function(mapping) {
var fens = this._fens.map(mapping);
return new Mesh({ fens: fens, gcells: this._gcells.clone() });
};
/**
*
* Return extruded mesh.
* @param {Array} hList - A list of values for each extrude layer.
* @param {Array} flags - Flags for each layer, falsy value means do
* not create cells in this layer.
* @returns {module:mesh.Mesh}
*/
exports.Mesh.prototype.extrude = function(hList, flags) {
if (!isArray(hList) || !isArray(flags) || hList.length != flags.length)
throw new Error('Mesh#extrude(hList, flags): hList and flags ' +
'must be array of same length.');
var newFens = this._fens.extrude(hList);
var newGcells = this._gcells.extrude(flags);
return new Mesh({ fens: newFens, gcells: newGcells });
};
/**
*
* Return subdivied mesh.
* @returns {module:mesh.Mesh} - subdivided mesh
*/
exports.Mesh.prototype.subdivide = function() {
if (this._gcells.type() === 'Q4') return this.subdivideQ4();
return this;
};
exports.Mesh.prototype.subdivideQ4 = function() {
var fens = this._fens;
var N = fens.count();
var gcells = this._gcells;
var topology = gcells.topology();
var q4Cells = topology.getCellsInDim(2);
var l2Cells = topology.getCellsInDim(1);
var idx = N;
var q4CellCenters = q4Cells.map(function(conn) {
var n1 = fens.xyzAt(conn[0]);
var n2 = fens.xyzAt(conn[1]);
var n3 = fens.xyzAt(conn[2]);
var n4 = fens.xyzAt(conn[3]);
var x = 0.25 * (n1[0] + n2[0] + n3[0] + n4[0]);
var y = 0.25 * (n1[1] + n2[1] + n3[1] + n4[1]);
return { index: idx++, xy: [x, y] };
});
var hashEdge = function(n1, n2) {
return '' + Math.min(n1, n2) + Math.max(n1, n2);
};
var l2CellCenters = l2Cells.map(function(conn) {
var n1 = fens.xyzAt(conn[0]);
var n2 = fens.xyzAt(conn[1]);
var x = 0.5 * (n1[0] + n2[0]);
var y = 0.5 * (n1[1] + n2[1]);
return {
key: hashEdge(conn[0], conn[1]),
index: idx++,
xy: [x, y]
};
});
var addedFens = new FeNodeSet({
xyz: q4CellCenters.map(function(p) {
return p.xy;
}).concat(l2CellCenters.map(function(p) {
return p.xy;
}))
});
var newFens = fens.combineWith(addedFens);
var newConn = [];
var l2CellCentersMap = l2CellCenters.reduce(function(sofar, obj) {
sofar[obj.key] = obj;
return sofar;
}, {});
q4Cells.forEach(function(cell, i) {
var n1 = cell[0], n2 = cell[1];
var n3 = cell[2], n4 = cell[3];
var n12 = l2CellCentersMap[hashEdge(n1, n2)].index;
var n23 = l2CellCentersMap[hashEdge(n2, n3)].index;
var n34 = l2CellCentersMap[hashEdge(n3, n4)].index;
var n41 = l2CellCentersMap[hashEdge(n4, n1)].index;
var nCentroid = q4CellCenters[i].index;
newConn.push([n1, n12, nCentroid, n41]);
newConn.push([n41, nCentroid, n34, n4]);
newConn.push([n12, n2, n23, nCentroid]);
newConn.push([nCentroid, n23, n3, n34]);
});
var newGCellSet = new Q4({
conn: newConn
});
return new Mesh({
fens: newFens,
gcells: newGCellSet
});
};
/**
* Creates a L-shaped domain using 3 quads.
* @returns {module:mesh.Mesh}
*/
exports.L2x2 = function L2x2() {
var fens, gcells;
fens = new FeNodeSet({
xyz: [
[1/2, 0],
[1, 0],
[1, 1/2],
[1/2, 1/2],
[1, 1],
[1/2, 1],
[0, 1],
[0, 1/2]
]
});
gcells = new Q4({
conn: [
[0, 1, 2, 3],
[3, 2, 4, 5],
[3, 5, 6, 7]
]
});
return new Mesh({ fens: fens, gcells: gcells });
};
var L2x2 = exports.L2x2;
/**
* Creates a L2 block mesh.
* @param {Number} w - width in x direction.
* @param {Int} nx - number of divisions in x direction.
* @returns {module:mesh.Mesh}
*/
exports.L2Block = function(w, nx) {
var p0d = new Mesh({
xyz: [ [] ],
gcells: new P1({
conn: [ [0] ]
})
});
var hList = array1d(nx, w/nx);
var flags = array1d(nx, true);
return p0d.extrude(hList, flags);
};
/**
* Creates a L2 block mesh.
* @param {Number} w - width in x direction.
* @param {Number} l - length in y direction.
* @param {Int} nx - number of divisions in x direction.
* @param {Int} ny - number of divisions in y direction.
* @returns {module:mesh.Mesh}
*/
exports.Q4Block = function(w, l, nx, ny) {
var l2 = exports.L2Block(w, nx);
var hList = array1d(ny, l/ny);
var flags = array1d(ny, true);
return l2.extrude(hList, flags);
};
/**
* Creates a H8 block mesh.
* @param {Number} w - width in x direction.
* @param {Number} l - length in y direction.
* @param {Number} h - height in z direction.
* @param {Int} nx - number of divisions in x direction.
* @param {Int} ny - number of divisions in y direction.
* @param {Int} nz - number of divisions in z direction.
* @returns {module:mesh.Mesh}
*/
exports.H8Block = function(w, l, h, nx, ny, nz) {
var q4 = exports.Q4Block(w, l, nx, ny);
var hList = array1d(nz, h/nz);
var flags = array1d(nz, true);
return q4.extrude(hList, flags);
};