/** * @brief Normalizes all FSR scalar fluxes and Track boundary angular * fluxes to the total fission source (times \f$ \nu \f$). */ void CPUSolver::normalizeFluxes() { FP_PRECISION* nu_sigma_f; FP_PRECISION volume; FP_PRECISION tot_fission_source; FP_PRECISION norm_factor; /* Compute total fission source for each FSR, energy group */ #pragma omp parallel for private(volume, nu_sigma_f) \ reduction(+:tot_fission_source) schedule(guided) for (int r=0; r < _num_FSRs; r++) { /* Get pointers to important data structures */ nu_sigma_f = _FSR_materials[r]->getNuSigmaF(); volume = _FSR_volumes[r]; for (int e=0; e < _num_groups; e++) _fission_sources(r,e) = nu_sigma_f[e] * _scalar_flux(r,e) * volume; } /* Compute the total fission source */ tot_fission_source = pairwise_sum<FP_PRECISION>(_fission_sources, _num_FSRs*_num_groups); /* Normalize scalar fluxes in each FSR */ norm_factor = 1.0 / tot_fission_source; log_printf(DEBUG, "Tot. Fiss. Src = %f, Normalization factor = %f", tot_fission_source, norm_factor); #pragma omp parallel for schedule(guided) for (int r=0; r < _num_FSRs; r++) { for (int e=0; e < _num_groups; e++) _scalar_flux(r,e) *= norm_factor; } /* Normalize angular boundary fluxes for each Track */ #pragma omp parallel for schedule(guided) for (int i=0; i < _tot_num_tracks; i++) { for (int j=0; j < 2; j++) { for (int p=0; p < _num_polar; p++) { for (int e=0; e < _num_groups; e++) { _boundary_flux(i,j,p,e) *= norm_factor; } } } } return; }
/** * @brief Zero each Track's boundary fluxes for each energy group and polar * angle in the "forward" and "reverse" directions. */ void CPUSolver::zeroTrackFluxes() { #pragma omp parallel for schedule(guided) for (int t=0; t < _tot_num_tracks; t++) { for (int d=0; d < 2; d++) { for (int p=0; p < _num_polar; p++) { for (int e=0; e < _num_groups; e++) { _boundary_flux(t,d,p,e) = 0.0; } } } } return; }
/** * @brief Updates the boundary flux for a Track given boundary conditions. * @details For reflective boundary conditions, the outgoing boundary flux * for the Track is given to the reflecting Track. For vacuum * boundary conditions, the outgoing flux tallied as leakage. * @param track_id the ID number for the Track of interest * @param azim_index a pointer to the azimuthal angle index for this segment * @param direction the Track direction (forward - true, reverse - false) * @param track_flux a pointer to the Track's outgoing angular flux */ void VectorizedSolver::transferBoundaryFlux(int track_id, int azim_index, bool direction, FP_PRECISION* track_flux) { int start; bool bc; FP_PRECISION* track_leakage; int track_out_id; /* Extract boundary conditions for this Track and the pointer to the * outgoing reflective Track, and index into the leakage array */ /* For the "forward" direction */ if (direction) { start = _tracks[track_id]->isReflOut() * _polar_times_groups; track_leakage = &_boundary_leakage(track_id,0); track_out_id = _tracks[track_id]->getTrackOut()->getUid(); bc = _tracks[track_id]->getBCOut(); } /* For the "reverse" direction */ else { start = _tracks[track_id]->isReflIn() * _polar_times_groups; track_leakage = &_boundary_leakage(track_id,_polar_times_groups); track_out_id = _tracks[track_id]->getTrackIn()->getUid(); bc = _tracks[track_id]->getBCIn(); } FP_PRECISION* track_out_flux = &_boundary_flux(track_out_id,0,0,start); /* Loop over polar angles and energy groups */ for (int p=0; p < _num_polar; p++) { /* Loop over each energy group vector length */ for (int v=0; v < _num_vector_lengths; v++) { /* Loop over energy groups within this vector */ #pragma simd vectorlength(VEC_LENGTH) for (int e=v*VEC_LENGTH; e < (v+1)*VEC_LENGTH; e++) { track_out_flux(p,e) = track_flux(p,e) * bc; track_leakage(p,e) = track_flux(p,e) * _polar_weights(azim_index,p) * (!bc); } } } }
/** * @brief Updates the boundary flux for a Track given boundary conditions. * @details For reflective boundary conditions, the outgoing boundary flux * for the Track is given to the reflecting Track. For vacuum * boundary conditions, the outgoing flux tallied as leakage. * @param track_id the ID number for the Track of interest * @param azim_index a pointer to the azimuthal angle index for this segment * @param direction the Track direction (forward - true, reverse - false) * @param track_flux a pointer to the Track's outgoing angular flux */ void CPUSolver::transferBoundaryFlux(int track_id, int azim_index, bool direction, FP_PRECISION* track_flux) { int start; int bc; FP_PRECISION* track_leakage; int track_out_id; /* Extract boundary conditions for this Track and the pointer to the * outgoing reflective Track, and index into the leakage array */ /* For the "forward" direction */ if (direction) { start = _tracks[track_id]->isReflOut() * _polar_times_groups; bc = (int)_tracks[track_id]->getBCOut(); track_leakage = &_boundary_leakage(track_id,0); track_out_id = _tracks[track_id]->getTrackOut()->getUid(); } /* For the "reverse" direction */ else { start = _tracks[track_id]->isReflIn() * _polar_times_groups; bc = (int)_tracks[track_id]->getBCIn(); track_leakage = &_boundary_leakage(track_id,_polar_times_groups); track_out_id = _tracks[track_id]->getTrackIn()->getUid(); } FP_PRECISION* track_out_flux = &_boundary_flux(track_out_id,0,0,start); /* Loop over polar angles and energy groups */ for (int e=0; e < _num_groups; e++) { for (int p=0; p < _num_polar; p++) { track_out_flux(p,e) = track_flux(p,e) * bc; track_leakage(p,e) = track_flux(p,e) * _polar_weights(azim_index,p) * (!bc); } } }
/** * @brief Updates the boundary flux for a Track given boundary conditions. * @details For reflective and periodic boundary conditions, the outgoing * boundary flux for the Track is given to the corresponding reflecting * or periodic Track. For vacuum boundary conditions, the outgoing flux * is tallied as leakage. * @param track_id the ID number for the Track of interest * @param azim_index a pointer to the azimuthal angle index for this segment * @param direction the Track direction (forward - true, reverse - false) * @param track_flux a pointer to the Track's outgoing angular flux */ void VectorizedSolver::transferBoundaryFlux(int track_id, int azim_index, bool direction, FP_PRECISION* track_flux) { int start; bool transfer_flux; int track_out_id; /* For the "forward" direction */ if (direction) { start = _tracks[track_id]->isNextOut() * _polar_times_groups; transfer_flux = _tracks[track_id]->getTransferFluxOut(); track_out_id = _tracks[track_id]->getTrackOut()->getUid(); } /* For the "reverse" direction */ else { start = _tracks[track_id]->isNextIn() * _polar_times_groups; transfer_flux = _tracks[track_id]->getTransferFluxIn(); track_out_id = _tracks[track_id]->getTrackIn()->getUid(); } FP_PRECISION* track_out_flux = &_boundary_flux(track_out_id,0,0,start); /* Loop over polar angles and energy groups */ for (int p=0; p < _num_polar; p++) { /* Loop over each energy group vector length */ for (int v=0; v < _num_vector_lengths; v++) { /* Loop over energy groups within this vector */ #pragma simd vectorlength(VEC_LENGTH) for (int e=v*VEC_LENGTH; e < (v+1)*VEC_LENGTH; e++) track_out_flux(p,e) = track_flux(p,e) * transfer_flux; } } }
/** * @brief This method performs one transport sweep of all azimuthal angles, * Tracks, Track segments, polar angles and energy groups. * @details The method integrates the flux along each Track and updates the * boundary fluxes for the corresponding output Track, while updating * the scalar flux in each flat source region. */ void CPUSolver::transportSweep() { int tid; int min_track, max_track; Track* curr_track; int azim_index; int num_segments; segment* curr_segment; segment* segments; FP_PRECISION* track_flux; log_printf(DEBUG, "Transport sweep with %d OpenMP threads", _num_threads); /* Initialize flux in each FSr to zero */ flattenFSRFluxes(0.0); if (_cmfd->getMesh()->getCmfdOn()) zeroSurfaceCurrents(); /* Loop over azimuthal angle halfspaces */ for (int i=0; i < 2; i++) { /* Compute the minimum and maximum Track IDs corresponding to * this azimuthal angular halfspace */ min_track = i * (_tot_num_tracks / 2); max_track = (i + 1) * (_tot_num_tracks / 2); /* Loop over each thread within this azimuthal angle halfspace */ #pragma omp parallel for private(curr_track, azim_index, num_segments, \ curr_segment, segments, track_flux, tid) schedule(guided) for (int track_id=min_track; track_id < max_track; track_id++) { tid = omp_get_thread_num(); /* Initialize local pointers to important data structures */ curr_track = _tracks[track_id]; azim_index = curr_track->getAzimAngleIndex(); num_segments = curr_track->getNumSegments(); segments = curr_track->getSegments(); track_flux = &_boundary_flux(track_id,0,0,0); /* Loop over each Track segment in forward direction */ for (int s=0; s < num_segments; s++) { curr_segment = &segments[s]; scalarFluxTally(curr_segment, azim_index, track_flux, &_thread_fsr_flux(tid),true); } /* Transfer boundary angular flux to outgoing Track */ transferBoundaryFlux(track_id, azim_index, true, track_flux); /* Loop over each Track segment in reverse direction */ track_flux += _polar_times_groups; for (int s=num_segments-1; s > -1; s--) { curr_segment = &segments[s]; scalarFluxTally(curr_segment, azim_index, track_flux, &_thread_fsr_flux(tid),false); } /* Transfer boundary angular flux to outgoing Track */ transferBoundaryFlux(track_id, azim_index, false, track_flux); } } return; }