/** * Version that assumes all voxels are in contact and able to diffuse * with matching voxels on other mesh */ void CubeMesh::matchAllEntries( const CubeMesh* other, vector< VoxelJunction >& ret ) const { ret.clear(); unsigned int min = m2s_.size(); if ( min > other->m2s_.size() ) min = other->m2s_.size(); ret.resize( min ); for ( unsigned int i = 0; i < min; ++i ) { ret[i] = VoxelJunction( i, i ); } }
void CylBase::matchCubeMeshEntries( const ChemCompt* compt, const CylBase& parent, unsigned int startIndex, double granularity, vector< VoxelJunction >& ret, bool useCylinderCurve, bool useCylinderCap ) const { const CubeMesh* other = dynamic_cast< const CubeMesh* >( compt ); assert( other ); const double EPSILON = 1e-18; Vec a( parent.x_ - x_, parent.y_ - y_, parent.z_ - z_ ); Vec u; Vec v; a.orthogonalAxes( u, v ); double h = selectGridSize( other->getDx(), parent.dia_/2, granularity ); double lambda = length_ / numDivs_; unsigned int num = floor( 0.1 + lambda / h ); // March along axis of cylinder. // q is the location of the point along axis. double rSlope = ( dia_ - parent.dia_ ) * 0.5 / length_; for ( unsigned int i = 0; i < numDivs_; ++i ) { vector< double >area( other->getNumEntries(), 0.0 ); if ( useCylinderCurve ) { for ( unsigned int j = 0; j < num; ++j ) { unsigned int m = i * num + j; double frac = ( m * h + h/2.0 ) / length_; double q0 = x_ + a.a0() * frac; double q1 = y_ + a.a1() * frac; double q2 = z_ + a.a2() * frac; // get radius of cylinder at this point. double r = dia_/2.0; if ( !isCylinder_ ) // Use the more complicated conic value r = parent.dia_/2.0 + frac * rSlope; fillPointsOnCircle( u, v, Vec( q0, q1, q2 ), h, r, area, other ); } } if ( useCylinderCap && i == numDivs_ - 1 ) { fillPointsOnDisc( u, v, Vec( x_, y_, z_ ), h, dia_/2.0, area, other ); } // Go through all cubeMesh entries and compute diffusion // cross-section. Assume this is through a membrane, so the // only factor relevant is area. Not the distance. for ( unsigned int k = 0; k < area.size(); ++k ) { if ( area[k] > EPSILON ) { ret.push_back( VoxelJunction( i + startIndex, k, area[k] )); } } } }
void CylMesh::matchCubeMeshEntries( const CubeMesh* other, vector< VoxelJunction >& ret ) const { const double EPSILON = 1e-18; Vec a( x1_ - x0_, y1_ - y0_, z1_ - z0_ ); Vec u; Vec v; a.orthogonalAxes( u, v ); double h = selectGridVolume( other->getDx() ); unsigned int num = floor( 0.1 + diffLength_ / h ); // March along axis of cylinder. // q is the location of the point along axis. for ( unsigned int i = 0; i < numEntries_; ++i ) { vector< double >area( other->getNumEntries(), 0.0 ); for ( unsigned int j = 0; j < num; ++j ) { unsigned int m = i * num + j; double frac = ( m * h + h/2.0 ) / totLen_; double q0 = x0_ + a.a0() * frac; double q1 = y0_ + a.a1() * frac; double q2 = z0_ + a.a2() * frac; // get radius of cylinder at this point. double r = r0_ + ( m * h + h / 2.0 ) * rSlope_; fillPointsOnCircle( u, v, Vec( q0, q1, q2 ), h, r, area, other ); } // Go through all cubeMesh entries and compute diffusion // cross-section. Assume this is through a membrane, so the // only factor relevant is area. Not the distance. for ( unsigned int k = 0; k < area.size(); ++k ) { if ( area[k] > EPSILON ) { ret.push_back( VoxelJunction( i, k, area[k] ) ); } } } }
/** * extendStencil adds voxels to the current stencil m_, to build up a * monolithic stencil that also handles the entries just past all the * boundaries. * This function may be called many times to deal with the addition of * multiple junctions. Before the first of these calls, the m_ matrix * should be set to the coreStencil_. */ void MeshCompt::extendStencil( const ChemCompt* other, const vector< VoxelJunction >& vj ) { // Maps from remote meshIndex (in vj) to local index of proxy voxel. map< unsigned int, unsigned int > meshMap; map< unsigned int, unsigned int >::iterator mmi; // Maps from local index of proxy voxel back to remote meshIndex. vector< unsigned int > meshBackMap; unsigned int coreSize = coreStencil_.nRows(); unsigned int oldSize = m_.nRows(); unsigned int newSize = oldSize; /// Organizes vj by voxel, that is, by row. vector< vector< VoxelJunction > > vvj( coreSize ); for ( vector< VoxelJunction >::const_iterator i = vj.begin(); i != vj.end(); ++i ) { mmi = meshMap.find( i->second ); if ( mmi == meshMap.end() ) { assert( i->first < coreSize ); meshBackMap.push_back( i->second ); meshMap[i->second] = newSize++; vvj[i->first].push_back( *i ); } } vector< vector< VoxelJunction > > vvjCol( newSize ); SparseMatrix< double > oldM = m_; m_.clear(); m_.setSize( newSize, newSize ); for ( unsigned int i = 0; i < newSize; ++i ) { vector< VoxelJunction > temp; if ( i < oldSize ) { // Copy over old matrix. const double* entry; const unsigned int* colIndex; unsigned int num = oldM.getRow( i, &entry, &colIndex ); temp.resize( num ); for ( unsigned int j = 0; j < num; ++j ) { temp[j].first = colIndex[j]; temp[j].diffScale = entry[j]; } } if ( i < coreSize ) { // Set up diffusion into proxy voxels. for ( vector< VoxelJunction >::const_iterator j = vvj[i].begin(); j != vvj[i].end(); ++j ) { unsigned int row = j->first; assert( row == i ); unsigned int col = meshMap[j->second]; assert( col >= oldSize ); temp.push_back( VoxelJunction( col, EMPTY, j->diffScale ) ); vvjCol[col].push_back( VoxelJunction( row, EMPTY, j->diffScale ) ); } } if ( i >= oldSize ) { // Set up diffusion from proxy to old voxels for ( vector< VoxelJunction >::const_iterator j = vvjCol[i].begin(); j != vvjCol[i].end(); ++j ) { temp.push_back( *j ); } } // Now we've filled in all the VoxelJunctions for the new row. sort( temp.begin(), temp.end() ); vector< double > e( temp.size() ); vector< unsigned int > c( temp.size() ); for ( unsigned int j = 0; j < temp.size(); ++j ) { e[j] = temp[j].diffScale; c[j] = temp[j].first; } m_.addRow( i, e, c ); } // Fill in the volumes of the external mesh entries for ( vector< unsigned int>::const_iterator i = meshBackMap.begin(); i != meshBackMap.end(); ++i ) { extendedMeshEntryVolume_.push_back( other->getMeshEntryVolume( *i ) ); } }
/** * checkAbut checks the intersect vector for the current position * ix, iy, iz, to determine how many diffusion terms to extract. It * then puts each of the extracted terms into the ret vector. There is * a minor efficiency for one and two diffusion terms as they are * encoded within the intersect vector. Higher-order surface alignments * require an in-line scan of neighboring voxels. * In all casesl the function inserts a flag indicating surface direction * into the diffScale * field of the VoxelJunction. 0 = x; 1 = y; 2 = z. */ void checkAbut( const vector< PII >& intersect, unsigned int ix, unsigned int iy, unsigned int iz, unsigned int nx, unsigned int ny, unsigned int nz, unsigned int meshIndex, vector< VoxelJunction >& ret ) { unsigned int index = ( iz * ny + iy ) * nx + ix; unsigned int localFlag = intersect[index].second; if ( localFlag == CubeMesh::EMPTY || localFlag == CubeMesh::SURFACE ) return; // Nothing to put into the ret vector if ( localFlag == CubeMesh::ABUTX ) { ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 0 )); } else if ( localFlag == CubeMesh::ABUTY ) { ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 1 )); } else if ( localFlag == CubeMesh::ABUTZ ) { ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 2 )); } else if ( localFlag == CubeMesh::MULTI ) { // go through all 6 cases. if ( ix > 0 ) { index = ( iz * ny + iy ) * nx + ix - 1; if ( intersect[index].second == CubeMesh::SURFACE ) ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 0 )); } if ( ix + 1 < nx ) { index = ( iz * ny + iy ) * nx + ix + 1; if ( intersect[index].second == CubeMesh::SURFACE ) ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 0 )); } if ( iy > 0 ) { index = ( iz * ny + iy -1 ) * nx + ix; if ( intersect[index].second == CubeMesh::SURFACE ) ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 1 )); } if ( iy + 1 < ny ) { index = ( iz * ny + iy + 1 ) * nx + ix; if ( intersect[index].second == CubeMesh::SURFACE ) ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 1 )); } if ( iz > 0 ) { index = ( (iz-1) * ny + iy ) * nx + ix; if ( intersect[index].second == CubeMesh::SURFACE ) ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 2 )); } if ( iz + 1 < nz ) { index = ( (iz+1) * ny + iy ) * nx + ix; if ( intersect[index].second == CubeMesh::SURFACE ) ret.push_back( VoxelJunction( intersect[index].first, meshIndex, 2 )); } } }
// Look for end-to-end diffusion, not sideways for now. // Very straightforward but tedious because of the different permutations. // Could readily add cylinder side to other end. void CylMesh::matchCylMeshEntries( const CylMesh* other, vector< VoxelJunction >& ret ) const { const double EPSILON = 1e-3; ret.clear(); // Should really estimate the distance from the centre of the smaller // cylinder cap disk to the plane of the larger disk, provided it is // within the radius of the disk. The subsequent calculations are the // same though. double dr1 = distance(x0_ - other->x0_, y0_ - other->y0_, z0_ - other->z0_ ); double dr2 = distance(x1_ - other->x1_, y1_ - other->y1_, z1_ - other->z1_ ); double dr3 = distance(x1_ - other->x0_, y1_ - other->y0_, z1_ - other->z0_ ); double dr4 = distance(x0_ - other->x1_, y0_ - other->y1_, z0_ - other->z1_ ); if ( dr1 <= dr2 && dr1 <= dr3 && dr1 <= dr4 ) { if ( ( dr1/totLen_ < EPSILON && dr1/other->totLen_ < EPSILON ) ) { double xda; if ( r0_ < other->r0_ ) xda = 2 * r0_ * r0_ * PI / ( diffLength_ + other->diffLength_ ); else xda = 2 * other->r0_ * other->r0_ * PI / ( diffLength_ + other->diffLength_ ); ret.push_back( VoxelJunction( 0, 0, xda ) ); } } else if ( dr2 <= dr3 && dr2 <= dr4 ) { if ( ( dr2/totLen_ < EPSILON && dr2/other->totLen_ < EPSILON ) ) { double xda; if ( r1_ < other->r1_ ) xda = 2 * r1_ * r1_ * PI / ( diffLength_ + other->diffLength_ ); else xda = 2 * other->r1_ * other->r1_ * PI / ( diffLength_ + other->diffLength_ ); ret.push_back( VoxelJunction( numEntries_ - 1, other->numEntries_ - 1, xda ) ); } } else if ( dr3 <= dr4 ) { if ( ( dr3/totLen_ < EPSILON && dr3/other->totLen_ < EPSILON ) ) { double xda; if ( r1_ < other->r0_ ) xda = 2 * r1_ * r1_ * PI / ( diffLength_ + other->diffLength_ ); else xda = 2 * other->r0_ * other->r0_ * PI / ( diffLength_ + other->diffLength_ ); ret.push_back( VoxelJunction( numEntries_ - 1, 0, xda ) ); } } else { if ( ( dr4/totLen_ < EPSILON && dr4/other->totLen_ < EPSILON ) ) { double xda; if ( r0_ < other->r1_ ) xda = 2 * r0_ * r0_ * PI / ( diffLength_ + other->diffLength_ ); else xda = 2 * other->r1_ * other->r1_ * PI / ( diffLength_ + other->diffLength_ ); ret.push_back( VoxelJunction( 0, other->numEntries_ - 1, xda )); } } }