/* * Copyright (C) 2012 Thorsten Liebig (Thorsten.Liebig@gmx.de) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sar_calculation.h" #include "cfloat" #include "array_ops.h" SAR_Calculation::SAR_Calculation() { m_Vx_Used = NULL; m_Vx_Valid = NULL; m_DebugLevel = 1; SetAveragingMethod(SIMPLE); Reset(); } void SAR_Calculation::Reset() { Delete3DArray(m_Vx_Used,m_numLines); m_Vx_Used = NULL; Delete3DArray(m_Vx_Valid,m_numLines); m_Vx_Valid = NULL; m_avg_mass = 0; m_numLines[0]=m_numLines[1]=m_numLines[2]=0; m_cellWidth[0]=m_cellWidth[1]=m_cellWidth[2]=NULL; m_cell_volume = NULL; m_cell_density = NULL; m_cell_conductivity = NULL; m_E_field = NULL; m_J_field = NULL; Delete3DArray(m_Vx_Used,m_numLines); m_Vx_Used = NULL; Delete3DArray(m_Vx_Valid,m_numLines); m_Vx_Valid = NULL; } void SAR_Calculation::SetAveragingMethod(SARAveragingMethod method) { if (method==IEEE_C95_3) { m_massTolerance = 0.0001; m_maxMassIterations = 100; m_maxBGRatio = 0.1; m_markPartialAsUsed = false; m_UnusedRelativeVolLimit = 1.05; m_IgnoreFaceValid = false; return; } else if (method==IEEE_62704) { m_massTolerance = 0.05; m_maxMassIterations = 100; m_maxBGRatio = 1; m_markPartialAsUsed = true; m_UnusedRelativeVolLimit = 1; m_IgnoreFaceValid = false; return; } else if (method==SIMPLE) { m_massTolerance = 0.05; m_maxMassIterations = 100; m_maxBGRatio = 1; m_markPartialAsUsed = true; m_UnusedRelativeVolLimit = 1; m_IgnoreFaceValid = true; return; } // unknown method, fallback to simple SetAveragingMethod(SIMPLE); } void SAR_Calculation::SetNumLines(unsigned int numLines[3]) { Delete3DArray(m_Vx_Used,m_numLines); m_Vx_Used = NULL; Delete3DArray(m_Vx_Valid,m_numLines); m_Vx_Valid = NULL; for (int n=0;n<3;++n) m_numLines[n]=numLines[n]; } void SAR_Calculation::SetCellWidth(float* cellWidth[3]) { for (int n=0;n<3;++n) m_cellWidth[n]=cellWidth[n]; } float*** SAR_Calculation::CalcSAR(float*** SAR) { if (CheckValid()==false) { cerr << "SAR_Calculation::CalcSAR: SAR calculation is invalid due to missing values... Abort..." << endl; return NULL; } if (m_avg_mass<=0) return CalcLocalSAR(SAR); return CalcAveragedSAR(SAR); } bool SAR_Calculation::CheckValid() { for (int n=0;n<3;++n) if (m_cellWidth[n]==NULL) return false; if (m_E_field==NULL) return false; if ((m_J_field==NULL) && (m_cell_conductivity==NULL)) return false; if (m_cell_density==NULL) return false; if (m_avg_mass<0) return false; return true; } double SAR_Calculation::CalcSARPower() { if (CheckValid()==false) { cerr << "SAR_Calculation::CalcSARPower: SAR calculation is invalid due to missing values... Abort..." << endl; return 0; } double power=0; unsigned int pos[3]; for (pos[0]=0; pos[0]0) { ++m_Valid; SAR[pos[0]][pos[1]][pos[2]] = CalcLocalPowerDensity(pos)/m_cell_density[pos[0]][pos[1]][pos[2]]; } else { ++m_AirVoxel; SAR[pos[0]][pos[1]][pos[2]] = 0; } } } } return SAR; } int SAR_Calculation::FindFittingCubicalMass(unsigned int pos[3], float box_size, unsigned int start[3], unsigned int stop[3], float partial_start[3], float partial_stop[3], float &mass, float &volume, float &bg_ratio, int disabledFace, bool ignoreFaceValid) { unsigned int mass_iterations = 0; float old_mass=0; float old_box_size=0; bool face_valid; bool mass_valid; bool voxel_valid; //iterate over cubical sizes to find fitting volume to mass while (mass_iterations increasing the box will not yield a valid cube return 1; } else if ((face_valid==false || bg_ratio>=m_maxBGRatio) && (mass_valid)) { // this is an invalid cube with a valid total mass if (ignoreFaceValid) return 0; return 2; } if (voxel_valid) { // valid cube found return 0; } // if no valid or finally invalid cube is found, calculate an alternaive cube size if (mass_iterations==0) { // on first interation, try a relative resize old_box_size=box_size; box_size*=pow(m_avg_mass/mass,1.0/3.0); } else { // on later interations, try a newton approach float new_box_size = box_size - (mass-m_avg_mass)/(mass-old_mass)*(box_size-old_box_size); old_box_size = box_size; box_size = new_box_size; } old_mass=mass; ++mass_iterations; } // m_maxMassIterations iterations are exhausted... return -1; } bool SAR_Calculation::GetCubicalMass(unsigned int pos[3], float box_size, unsigned int start[3], unsigned int stop[3], float partial_start[3], float partial_stop[3], float &mass, float &volume, float &bg_ratio, int disabledFace) { if ((box_size<=0) || isnan(box_size) || isinf(box_size)) { cerr << "SAR_Calculation::GetCubicalMass: critical error: invalid averaging box size!! EXIT" << endl; exit(-1); } bool face_valid=true; for (int n=0;n<3;++n) { // check start position start[n]=pos[n]; partial_start[n]=1; float dist=m_cellWidth[n][pos[n]]/2; if (disabledFace==2*n) dist=box_size; else { while (dist=0) { mass += CellMass(f_pos)*weight[0]*weight[1]*weight[2]; power_mass += CalcLocalPowerDensity(f_pos) * CellVolume(f_pos)*weight[0]*weight[1]*weight[2]; } } } } float vx_SAR = power_mass/mass; if (SAR==NULL) return vx_SAR; SAR[pos[0]][pos[1]][pos[2]]=vx_SAR; if (assignUsed==false) return vx_SAR; // assign SAR to all used voxel bool is_partial[3]; for (f_pos[0]=start[0];f_pos[0]<=stop[0];++f_pos[0]) { if ( ((f_pos[0]==start[0]) && (partial_start[0]!=1)) || ((f_pos[0]==stop[0]) && (partial_stop[0]!=1)) ) is_partial[0]=true; else is_partial[0]=false; for (f_pos[1]=start[1];f_pos[1]<=stop[1];++f_pos[1]) { if ( ((f_pos[1]==start[1]) && (partial_start[1]!=1)) || ((f_pos[1]==stop[1]) && (partial_stop[1]!=1)) ) is_partial[1]=true; else is_partial[1]=false; for (f_pos[2]=start[2];f_pos[2]<=stop[2];++f_pos[2]) { if ( ((f_pos[2]==start[2]) && (partial_start[2]!=1)) || ((f_pos[2]==stop[2]) && (partial_stop[2]!=1)) ) is_partial[2]=true; else is_partial[2]=false; if ( (!is_partial[0] && !is_partial[1] && !is_partial[2]) || m_markPartialAsUsed) { if (!m_Vx_Valid[f_pos[0]][f_pos[1]][f_pos[2]] && (m_cell_density[f_pos[0]][f_pos[1]][f_pos[2]]>0)) { m_Vx_Used[f_pos[0]][f_pos[1]][f_pos[2]]=true; SAR[f_pos[0]][f_pos[1]][f_pos[2]]=max(SAR[f_pos[0]][f_pos[1]][f_pos[2]], vx_SAR); } } } } } return vx_SAR; } float*** SAR_Calculation::CalcAveragedSAR(float*** SAR) { unsigned int pos[3]; m_Vx_Used = Create3DArray(m_numLines); m_Vx_Valid = Create3DArray(m_numLines); float voxel_volume; float total_mass; unsigned int start[3]; unsigned int stop[3]; float partial_start[3]; float partial_stop[3]; float bg_ratio; int EC=0; // debug counter unsigned int cnt_case1=0; unsigned int cnt_case2=0; unsigned int cnt_NoConvergence=0; m_Valid=0; m_Used=0; m_Unused=0; m_AirVoxel=0; for (pos[0]=0; pos[0]0) { cerr << "SAR_Calculation::CalcAveragedSAR: Warning, for some voxel a valid averaging cube could not be found (no convergence)... " << endl; } if (m_DebugLevel>0) { cerr << "Number of invalid cubes (case 1): " << cnt_case1 << endl; cerr << "Number of invalid cubes (case 2): " << cnt_case2 << endl; cerr << "Number of invalid cubes (falied to converge): " << cnt_NoConvergence << endl; } // count all used and unused etc. + special handling of unused voxels!! m_Used=0; m_Unused=0; for (pos[0]=0;pos[0]0) && !m_Vx_Valid[pos[0]][pos[1]][pos[2]] && !m_Vx_Used[pos[0]][pos[1]][pos[2]]) { ++m_Unused; SAR[pos[0]][pos[1]][pos[2]] = 0; float unused_volumes[6]; float unused_SAR[6]; float min_Vol=FLT_MAX; // special handling of unused voxels: for (int n=0;n<6;++n) { EC = FindFittingCubicalMass(pos, pow(m_avg_mass/m_cell_density[pos[0]][pos[1]][pos[2]],1.0/3.0)/2, start, stop, partial_start, partial_stop, total_mass, unused_volumes[n], bg_ratio, n, true); if ((EC!=0) && (EC!=2)) { // this should not happen cerr << "SAR_Calculation::CalcAveragedSAR: Error handling unused voxels, can't find fitting cubical averaging volume' " << endl; unused_SAR[n]=0; } else { unused_SAR[n]=CalcCubicalSAR(NULL, pos, start, stop, partial_start, partial_stop, false); min_Vol = min(min_Vol,unused_volumes[n]); } } for (int n=0;n<6;++n) { if (unused_volumes[n]<=m_UnusedRelativeVolLimit*min_Vol) SAR[pos[0]][pos[1]][pos[2]] = max(SAR[pos[0]][pos[1]][pos[2]],unused_SAR[n]); } } } } } if (m_Valid+m_Used+m_Unused+m_AirVoxel!=m_numLines[0]*m_numLines[1]*m_numLines[2]) { cerr << "SAR_Calculation::CalcAveragedSAR: critical error, mismatch in voxel status count... EXIT" << endl; exit(1); } if (m_DebugLevel>0) cerr << "SAR_Calculation::CalcAveragedSAR: Stats: Valid=" << m_Valid << " Used=" << m_Used << " Unused=" << m_Unused << " Air-Voxel=" << m_AirVoxel << endl; return SAR; } float SAR_Calculation::CellVolume(unsigned int pos[3]) { if (m_cell_volume) return m_cell_volume[pos[0]][pos[1]][pos[2]]; float volume=1; for (int n=0;n<3;++n) volume*=m_cellWidth[n][pos[n]]; return volume; } float SAR_Calculation::CellMass(unsigned int pos[3]) { return m_cell_density[pos[0]][pos[1]][pos[2]]*CellVolume(pos); }