34 setInputOutputProperties();
36 setNumericalProperties();
45 std::fill(m_timers.begin(), m_timers.end(), -1);
48 NEW_TIMER_GROUP_NOCREATE(m_timerGroup,
"AcaSolver (solverId = " + std::to_string(m_solverId) +
")");
49 NEW_TIMER_NOCREATE(m_timers[Timers::SolverType],
"total object lifetime", m_timerGroup);
50 RECORD_TIMER_START(m_timers[Timers::SolverType]);
53 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Run],
"run", m_timers[Timers::SolverType]);
55 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::InitParallelization],
"initParallelization", m_timers[Timers::Run]);
56 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::ReadSurfaceData],
"readSurfaceData", m_timers[Timers::Run]);
58 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::InitObservers],
"initObservers", m_timers[Timers::Run]);
59 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::CalcSourceTerms],
"calcSourceTerms", m_timers[Timers::Run]);
60 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::WindowAndTransformSources],
"windowAndTransformSources",
61 m_timers[Timers::Run]);
62 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::CalcSurfaceIntegrals],
"calcSurfaceIntegrals", m_timers[Timers::Run]);
63 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::CombineSurfaceIntegrals],
"combineSurfaceIntegrals", m_timers[Timers::Run]);
64 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::BacktransformObservers],
"backtransformObservers", m_timers[Timers::Run]);
65 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::SaveObserverSignals],
"saveObserverSignals", m_timers[Timers::Run]);
66 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Postprocessing],
"postprocessing", m_timers[Timers::Run]);
76 std::vector<MInt> timerIds_;
77 timerIds_.reserve(11);
78 timerIds_.emplace_back(m_timers[Timers::Run]);
79 timerIds_.emplace_back(m_timers[Timers::InitParallelization]);
80 timerIds_.emplace_back(m_timers[Timers::ReadSurfaceData]);
81 timerIds_.emplace_back(m_timers[Timers::InitObservers]);
82 timerIds_.emplace_back(m_timers[Timers::CalcSourceTerms]);
83 timerIds_.emplace_back(m_timers[Timers::WindowAndTransformSources]);
84 timerIds_.emplace_back(m_timers[Timers::CalcSurfaceIntegrals]);
85 timerIds_.emplace_back(m_timers[Timers::CombineSurfaceIntegrals]);
86 timerIds_.emplace_back(m_timers[Timers::BacktransformObservers]);
87 timerIds_.emplace_back(m_timers[Timers::SaveObserverSignals]);
88 timerIds_.emplace_back(m_timers[Timers::Postprocessing]);
89 const MInt noTimers = timerIds_.size();
91 std::vector<MFloat> timerValues_;
92 timerValues_.reserve(noTimers);
93 for(
MInt i = 0; i < noTimers; i++) {
94 timerValues_.emplace_back(RETURN_TIMER_TIME(timerIds_[i]));
98 AT_,
"MPI_IN_PLACE",
"timerValues_");
99 for(
MInt i = 0; i < noTimers; i++) {
100 SET_RECORD(timerIds_[i], timerValues_[i]);
117 m_inputDir = Context::getSolverProperty<MString>(
"inputDir", m_solverId, AT_);
118 m_inputDir = testcaseDir() + m_inputDir;
128 m_observerFileName =
"";
129 m_observerFileName = Context::getSolverProperty<MString>(
"observerFileName", m_solverId, AT_, &m_observerFileName);
139 m_generateObservers =
false;
140 m_generateObservers = Context::getSolverProperty<MBool>(
"generateObservers", m_solverId, AT_, &m_generateObservers);
142 TERMM_IF_COND(m_observerFileName ==
"" && m_generateObservers ==
false,
143 "No observer file provided and generation of observers disabled.");
154 m_outputFilePrefix =
"";
155 m_outputFilePrefix = Context::getSolverProperty<MString>(
"outputFilePrefix", m_solverId, AT_, &m_outputFilePrefix);
167 m_noSurfaces = Context::getSolverProperty<MInt>(
"noSurfaces", m_solverId, AT_, &m_noSurfaces);
172 if(noSurfaces() != 0 && noSurfaces() != noGivenSrfcIds) {
173 std::cerr <<
"WARNING: Mismatch between properties 'noSurfaces' and size of surfaceIds" << std::endl;
175 m_noSurfaces = noGivenSrfcIds;
176 TERMM_IF_COND(m_noSurfaces <= 0,
"Number of surfaces (noSurfaces) needs to be > 0");
177 m_surfaceIds.resize(noSurfaces());
178 for(
MInt id = 0;
id < noSurfaces();
id++) {
188 m_surfaceIds[
id] = Context::getSolverProperty<MInt>(
"surfaceIds", m_solverId, AT_,
id);
191 TERMM_IF_COND(m_noSurfaces <= 0,
"Number of surfaces (noSurfaces) needs to be > 0");
192 m_surfaceIds.resize(noSurfaces());
193 for(
MInt id = 0;
id < noSurfaces();
id++) {
194 m_surfaceIds[
id] =
id;
208 m_useMergedInputFile =
true;
209 m_useMergedInputFile =
210 Context::getSolverProperty<MBool>(
"useMergedInputFile", m_solverId, AT_, &m_useMergedInputFile);
211 if(m_useMergedInputFile ==
false) {
220 m_inputFileIndexStart = Context::getSolverProperty<MInt>(
"inputFileIndexStart", m_solverId, AT_);
228 m_inputFileIndexEnd = Context::getSolverProperty<MInt>(
"inputFileIndexEnd", m_solverId, AT_);
229 TERMM_IF_COND(m_inputFileIndexStart > m_inputFileIndexEnd,
230 "'inputFileIndexStart' must not be greater than 'm_inputFileIndexEnd'.");
243 m_noSamples = Context::getSolverProperty<MInt>(
"acaNoSamples", m_solverId, AT_, &m_noSamples);
254 m_sampleStride = Context::getSolverProperty<MInt>(
"acaSampleStride", m_solverId, AT_, &m_sampleStride);
265 m_sampleOffset = Context::getSolverProperty<MInt>(
"acaSampleOffset", m_solverId, AT_, &m_sampleOffset);
276 m_allowMultipleSurfacesPerRank = Context::getSolverProperty<MBool>(
"allowMultipleSurfacesPerRank", m_solverId, AT_,
277 &m_allowMultipleSurfacesPerRank);
287 m_generateSurfaceData =
false;
288 m_generateSurfaceData =
289 Context::getSolverProperty<MBool>(
"generateSurfaceData", m_solverId, AT_, &m_generateSurfaceData);
304 m_acaResultsNondimMode = 0;
305 m_acaResultsNondimMode =
306 Context::getSolverProperty<MInt>(
"acaResultsNondimMode", m_solverId, AT_, &m_acaResultsNondimMode);
316 m_storeSurfaceData =
false;
317 m_storeSurfaceData = Context::getSolverProperty<MBool>(
"storeSurfaceData", m_solverId, AT_, &m_storeSurfaceData);
320 m_noPostprocessingOps = 0;
323 m_postprocessingOps.resize(m_noPostprocessingOps, -1);
325 for(
MInt i = 0; i < m_noPostprocessingOps; i++) {
342 m_postprocessingOps[i] = Context::getSolverProperty<MInt>(
"postprocessingOps", m_solverId, AT_, i);
354 m_acaPostprocessingMode =
false;
355 m_acaPostprocessingMode =
356 Context::getSolverProperty<MBool>(
"acaPostprocessingMode", m_solverId, AT_, &m_acaPostprocessingMode);
358 if(m_acaPostprocessingMode) {
359 TERMM_IF_COND(m_noPostprocessingOps == 0,
"acaPostprocessingMode is enabled but noPostprocessingOps is zero.");
368 m_acaPostprocessingFile = Context::getSolverProperty<MString>(
"acaPostprocessingFile", m_solverId, AT_);
380 const MString propNameSymOrigin =
"symmetryOrigin";
390 const MString propNameSymNormal =
"symmetryNormal";
396 TERMM_IF_COND(propLength_symOrign % nDim != 0, propNameSymOrigin +
" has to be of a length of multiple nDim.");
397 TERMM_IF_COND(propLength_symNormal % nDim != 0, propNameSymNormal +
" has to be of a length of multiple nDim.");
398 const MInt noSym = propLength_symOrign / nDim;
399 TERMM_IF_COND(noSym != propLength_symNormal / nDim,
400 propNameSymOrigin +
" and " + propNameSymNormal +
" have to be of the same length.");
402 TERMM_IF_COND(noSym > 1,
"So far not more than 1 symmetry BC possible.");
403 m_symBc.resize(noSym);
404 for(
MInt i = 0; i < noSym; i++) {
405 MFloat lengthSymNormal = 0.0;
406 for(
MInt d = 0; d < nDim; d++) {
407 m_symBc[i].origin[d] = Context::getSolverProperty<MFloat>(propNameSymOrigin, m_solverId, AT_, i * nDim + d);
408 m_symBc[i].normal[d] = Context::getSolverProperty<MFloat>(propNameSymNormal, m_solverId, AT_, i * nDim + d);
409 lengthSymNormal +=
POW2(m_symBc[i].normal[d]);
411 lengthSymNormal = sqrt(lengthSymNormal);
412 for(
MInt d = 0; d < nDim; d++) {
413 m_symBc[i].normal[d] /= lengthSymNormal;
436 const MString methodName = Context::getSolverProperty<MString>(
"acousticMethod", solverId(), AT_);
437 m_acousticMethod = -1;
441 switch(m_acousticMethod) {
444 m_noVariables = FWH::noVars;
445 m_noComplexVariables = FWH::noComplexVars;
448 m_inputVars[
"u"] = FWH::U;
449 m_inputVars[
"v"] = FWH::V;
450 if constexpr(nDim == 3) {
451 m_inputVars[
"w"] = FWH::W;
453 m_inputVars[
"rho"] = FWH::RHO;
454 m_inputVars[
"p"] = FWH::P;
459 m_noVariables = FWH_APE::noVars;
460 m_noComplexVariables = FWH_APE::noComplexVars;
463 m_inputVars[
"u"] = FWH_APE::UP;
464 m_inputVars[
"v"] = FWH_APE::VP;
465 if constexpr(nDim == 3) {
466 m_inputVars[
"w"] = FWH_APE::WP;
468 m_inputVars[
"p"] = FWH_APE::PP;
473 TERMM(1,
"Invalid acoustic extrapolation method: '" + std::to_string(m_acousticMethod) +
"'");
497 m_fwhTimeShiftType = 0;
498 m_fwhTimeShiftType = Context::getSolverProperty<MInt>(
"fwhTimeShiftType", m_solverId, AT_, &m_fwhTimeShiftType);
512 m_fwhMeanStateType = 0;
513 m_fwhMeanStateType = Context::getSolverProperty<MInt>(
"fwhMeanStateType", m_solverId, AT_, &m_fwhMeanStateType);
534 m_windowType = Context::getSolverProperty<MInt>(
"windowType", m_solverId, AT_, &m_windowType);
551 m_transformationType = 1;
552 m_transformationType = Context::getSolverProperty<MInt>(
"transformationType", m_solverId, AT_, &m_transformationType);
555 m_surfaceWeightingFactor.resize(noSurfaces(), 1.0);
557 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
566 const MFloat weightingFactor = Context::getSolverProperty<MFloat>(
"surfaceWeightingFactor", solverId(), AT_, sId);
567 TERMM_IF_COND(weightingFactor < 0.0 || weightingFactor > 1.0,
568 "Error: surface weighting factor should be in range (0.0,1.0], is: "
569 + std::to_string(weightingFactor));
570 m_surfaceWeightingFactor[sId] = weightingFactor;
572 m_log <<
"Surface weighting factor for surface #" << m_surfaceIds[sId] <<
": " << m_surfaceWeightingFactor[sId]
576 m_log <<
"Surface weighting not enabled." << std::endl;
579 m_log <<
"Mach vector: ";
581 for(
MInt i = 0; i < nDim; i++) {
591 m_MaVec[i] = Context::getSolverProperty<MFloat>(
"Ma", m_solverId, AT_, &m_MaVec[i], i);
592 m_log << m_MaVec[i] <<
" ";
593 machSq +=
POW2(m_MaVec[i]);
595 const MFloat Mach = sqrt(machSq);
607 m_MaDim = Context::getSolverProperty<MFloat>(
"MaDim", m_solverId, AT_);
611 m_log <<
"; Ma = " << m_Ma <<
"; Ma_dim = " << m_MaDim << std::endl;
620 if constexpr(nDim == 3) {
621 if(!
approx(m_MaVec[2], 0.0, MFloatEps) && !
approx(m_MaVec[0], 0.0, MFloatEps)) {
622 theta = atan2(m_MaVec[2], m_MaVec[0]);
624 if(!
approx(m_MaVec[1], 0.0, MFloatEps)) {
625 alpha = asin(m_MaVec[1] / m_Ma);
627 m_transformationMatrix[0] =
cos(alpha) *
cos(theta);
628 m_transformationMatrix[1] = sin(alpha);
629 m_transformationMatrix[2] =
cos(alpha) * sin(theta);
630 m_transformationMatrix[3] = -sin(alpha) *
cos(theta);
631 m_transformationMatrix[4] =
cos(alpha);
632 m_transformationMatrix[5] = -sin(alpha) * sin(theta);
633 m_transformationMatrix[6] = -sin(theta);
634 m_transformationMatrix[7] = 0.0;
635 m_transformationMatrix[8] =
cos(theta);
636 }
else if constexpr(nDim == 2) {
637 if(!
approx(m_MaVec[1], 0.0, MFloatEps) && !
approx(m_MaVec[0], 0.0, MFloatEps)) {
638 theta = atan2(m_MaVec[1], m_MaVec[0]);
640 m_transformationMatrix[0] =
cos(theta);
641 m_transformationMatrix[1] = sin(theta);
642 m_transformationMatrix[2] = -sin(theta);
643 m_transformationMatrix[3] =
cos(theta);
645 MFloat flowAngle = std::sqrt(theta * theta + alpha * alpha);
646 if(
approx(flowAngle, 0.0, MFloatEps)) m_zeroFlowAngle =
true;
648 if(m_generateSurfaceData) {
666 m_sourceType = Context::getSolverProperty<MInt>(
"sourceType", m_solverId, AT_, &m_sourceType);
668 m_sourceParameters.perturbed = (m_acousticMethod ==
FWH_METHOD) ?
false :
true;
671 m_sourceParameters.Ma = Mach;
672 constexpr MFloat c0 = 1.0;
682 m_sourceParameters.rho0 = 1.0;
683 m_sourceParameters.rho0 = Context::getSolverProperty<MFloat>(
"rho0", m_solverId, AT_, &m_sourceParameters.rho0);
694 m_omega_factor = (nDim == 2) ? 4.0 : 4.0 / 46.0;
695 m_omega_factor = Context::getSolverProperty<MFloat>(
"omega_factor", m_solverId, AT_, &m_omega_factor);
705 m_sourceParameters.omega = m_omega_factor * PI * c0;
706 m_sourceParameters.omega = Context::getSolverProperty<MFloat>(
"omega", m_solverId, AT_, &m_sourceParameters.omega);
716 m_sourceParameters.beta = sqrt(1 - Mach * Mach);
717 m_sourceParameters.beta = Context::getSolverProperty<MFloat>(
"beta", m_solverId, AT_, &m_sourceParameters.beta);
720 m_sourceParameters.amplitude = (nDim == 2 ? 0.0001 : 0.01) * c0;
723 std::stringstream ss;
724 ss <<
"Source parameters" << std::endl;
725 ss <<
" perturbed: " << m_sourceParameters.perturbed << std::endl;
726 ss <<
" omega: " << m_sourceParameters.omega << std::endl;
727 ss <<
" rho0: " << m_sourceParameters.rho0 << std::endl;
728 ss <<
" beta: " << m_sourceParameters.beta << std::endl;
729 ss <<
" amplitude: " << m_sourceParameters.amplitude << std::endl;
730 ss <<
" Ma: " << m_sourceParameters.Ma << std::endl;
743 m_noSamples = Context::getSolverProperty<MInt>(
"generateSurfaceDataNoSamples", m_solverId, AT_);
744 m_totalNoSamples = m_noSamples;
749 if(m_noSamples % 2 > 0) {
750 m_noSamples = m_noSamples - 1;
751 if(domainId() == 0) {
752 std::cout <<
"Note: Number of Samples was uneven which could lead to mistakes. This is why the "
753 "new number of Samples is one less."
767 noPeriods = Context::getSolverProperty<MFloat>(
"noPeriods", m_solverId, AT_, &noPeriods);
768 TERMM_IF_COND(noPeriods < 1.0,
"The number of periods is below 1!");
778 m_dt = noPeriods * 2.0 * PI / (noSamples() * m_sourceParameters.omega);
779 m_dt = Context::getSolverProperty<MFloat>(
"timeStepSize", m_solverId, AT_, &m_dt);
780 const MFloat freq_max = m_sourceParameters.omega / (2 * PI);
782 TERMM_IF_COND(m_dt >= 1.0 / (2.0 * freq_max),
"Nyquist criterions is not met!");
785 if(domainId() == 0) {
786 std::cout <<
" >> NOTE: The maximum frequency is f_max = omega / (2*pi) = " << freq_max
787 <<
". The Nyquist criterion is ensured ( m_dt < 1 /(2*freq_max) ), since..." << std::endl;
788 std::cout <<
" >> m_dt = " << m_dt <<
" < " << 1.0 / (2.0 * freq_max) <<
" = 1 / (2*freq_max) <<" << std::endl;
792 if(domainId() == 0) {
793 std::cout <<
"Acoustic extrapolation method: " << methodName << std::endl;
794 std::cout <<
" * noVars = " << m_noVariables << std::endl;
795 std::cout <<
" * noComplexVars = " << m_noComplexVariables << std::endl;
797 std::cout <<
" * Input variables:" << std::endl;
798 for(
auto& var : m_inputVars) {
799 std::cout <<
" * " << var.first <<
", position " << var.second << std::endl;
809 RECORD_TIMER_START(m_timers[Timers::Run]);
812 initParallelization();
814 initConversionFactors();
822 initObserverPoints();
824 if(!m_acaPostprocessingMode) {
831 windowAndTransformSources();
836 calculateObserverInFrequency();
839 backtransformObservers();
842 calculateObserverInTime();
847 windowAndTransformObservers();
851 changeDimensionsObserverData();
854 saveObserverSignals();
858 postprocessObserverSignals();
864 loadObserverSignals();
867 postprocessObserverSignals();
870 RECORD_TIMER_STOP(m_timers[Timers::Run]);
881 RECORD_TIMER_START(m_timers[Timers::CalcSourceTerms]);
882 printMessage(
"- Calculate source terms");
888 m_log <<
"Using Frequency-Domain FW-H Formulation" << std::endl;
889 for(
MInt i = 0; i < totalNoSurfaceElements(); i++) {
890 calcSourceTermsFwhFreq(i);
893 m_log <<
"Using Farassat 1A Time-Domain FW-H Formulation" << std::endl;
894 for(
MInt i = 0; i < totalNoSurfaceElements(); i++) {
895 calcSourceTermsFwhTime(i);
898 TERMM(1,
"Unknown solverMethod");
901 RECORD_TIMER_STOP(m_timers[Timers::CalcSourceTerms]);
913 const MFloat*
const normal = &m_surfaceData.surfaceNormal(segmentId);
915 const MFloat dfdx = normal[0];
916 const MFloat dfdy = normal[1];
918 const MFloat Max = m_MaVec[0];
919 const MFloat May = m_MaVec[1];
922 const MFloat rho_inf = 1.0;
923 const MFloat p_inf = rho_inf * (c_inf * c_inf) / m_gamma;
924 const MFloat noSamplesInv = 1.0 / noSamples();
926 if constexpr(nDim == 3) {
928 const MFloat dfdz = normal[2];
930 const MFloat Maz = m_MaVec[2];
937 switch(m_fwhMeanStateType) {
945 for(
MInt t = 0; t < noSamples(); t++) {
946 uMean += m_surfaceData.variables(segmentId, FWH::U, t);
947 vMean += m_surfaceData.variables(segmentId, FWH::V, t);
948 wMean += m_surfaceData.variables(segmentId, FWH::W, t);
950 uMean *= noSamplesInv;
951 vMean *= noSamplesInv;
952 wMean *= noSamplesInv;
956 TERMM(1,
"fwh mean state type not implemented");
963 maia::parallelFor<false>(0, noSamples(), [=](
MInt t) {
965 std::vector<MInt> VarDim;
972 VarDim = {FWH::FX, FWH::FY, FWH::FZ, FWH::Q};
973 up = m_surfaceData.variables(segmentId, FWH::U, t) - uMean;
974 vp = m_surfaceData.variables(segmentId, FWH::V, t) - vMean;
975 wp = m_surfaceData.variables(segmentId, FWH::W, t) - wMean;
976 p = m_surfaceData.variables(segmentId, FWH::P, t);
977 rho = m_surfaceData.variables(segmentId, FWH::RHO, t);
979 VarDim = {FWH_APE::FX, FWH_APE::FY, FWH_APE::FZ, FWH_APE::Q};
980 up = m_surfaceData.variables(segmentId, FWH_APE::UP, t);
981 vp = m_surfaceData.variables(segmentId, FWH_APE::VP, t);
982 wp = m_surfaceData.variables(segmentId, FWH_APE::WP, t);
983 p = m_surfaceData.variables(segmentId, FWH_APE::PP, t) + p_inf;
984 rho = m_surfaceData.variables(segmentId, FWH_APE::PP, t) + rho_inf;
987 const MFloat f1 = (up + Max) * dfdx + (vp + May) * dfdy + (wp + Maz) * dfdz;
988 const MFloat f2 = Max * dfdx + May * dfdy + Maz * dfdz;
991 m_surfaceData.variables(segmentId, VarDim[0], t) = p * dfdx + rho * (up - Max) * f1 + Max * f2;
992 m_surfaceData.variables(segmentId, VarDim[1], t) = p * dfdy + rho * (vp - May) * f1 + May * f2;
993 m_surfaceData.variables(segmentId, VarDim[2], t) = p * dfdz + rho * (wp - Maz) * f1 + Maz * f2;
994 m_surfaceData.variables(segmentId, VarDim[3], t) = rho * f1 - f2;
1001 switch(m_fwhMeanStateType) {
1008 for(
MInt t = 0; t < noSamples(); t++) {
1009 uMean += m_surfaceData.variables(segmentId, FWH::U, t);
1010 vMean += m_surfaceData.variables(segmentId, FWH::V, t);
1012 uMean = uMean * noSamplesInv;
1013 vMean = vMean * noSamplesInv;
1017 TERMM(1,
"fwh mean state type not implemented");
1024 maia::parallelFor<false>(0, noSamples(), [=](
MInt t) {
1026 std::vector<MInt> VarDim;
1032 VarDim = {FWH::FX, FWH::FY, FWH::Q};
1033 up = m_surfaceData.variables(segmentId, FWH::U, t) - uMean;
1034 vp = m_surfaceData.variables(segmentId, FWH::V, t) - vMean;
1035 p = m_surfaceData.variables(segmentId, FWH::P, t);
1036 rho = m_surfaceData.variables(segmentId, FWH::RHO, t);
1038 VarDim = {FWH_APE::FX, FWH_APE::FY, FWH_APE::Q};
1039 up = m_surfaceData.variables(segmentId, FWH_APE::UP, t);
1040 vp = m_surfaceData.variables(segmentId, FWH_APE::VP, t);
1041 p = m_surfaceData.variables(segmentId, FWH_APE::PP, t) + p_inf;
1042 rho = m_surfaceData.variables(segmentId, FWH_APE::PP, t) + rho_inf;
1045 const MFloat f1 = (up + Max) * dfdx + (vp + May) * dfdy;
1046 const MFloat f2 = Max * dfdx + May * dfdy;
1049 m_surfaceData.variables(segmentId, VarDim[0], t) = p * dfdx + rho * (up - Max) * f1 + Max * f2;
1050 m_surfaceData.variables(segmentId, VarDim[1], t) = p * dfdy + rho * (vp - May) * f1 + May * f2;
1051 m_surfaceData.variables(segmentId, VarDim[2], t) = rho * f1 - f2;
1066 const MFloat*
const normal = &m_surfaceData.surfaceNormal(segmentId);
1067 const MFloat c_inf = 1.0;
1068 const MFloat rho_inf = 1.0;
1069 const MFloat p_inf = rho_inf * (c_inf * c_inf) / m_gamma;
1070 const MFloat noSamplesInv = 1.0 / noSamples();
1072 if constexpr(nDim == 3) {
1079 switch(m_fwhMeanStateType) {
1088 for(
MInt t = 0; t < noSamples(); t++) {
1089 uMean += m_surfaceData.variables(segmentId, FWH::U, t);
1090 vMean += m_surfaceData.variables(segmentId, FWH::V, t);
1091 wMean += m_surfaceData.variables(segmentId, FWH::W, t);
1092 pMean += m_surfaceData.variables(segmentId, FWH::P, t);
1094 uMean *= noSamplesInv;
1095 vMean *= noSamplesInv;
1096 wMean *= noSamplesInv;
1097 pMean *= noSamplesInv;
1101 TERMM(1,
"fwh mean state type not implemented");
1112 MFloat surfVX = -m_MaVec[0];
1113 MFloat surfVY = -m_MaVec[1];
1114 MFloat surfVZ = -m_MaVec[2];
1116 maia::parallelFor<false>(0, noSamples(), [=](
MInt tau) {
1118 m_surfaceData.variables(segmentId, FWH_TIME::surfMX, tau) = surfVX;
1119 m_surfaceData.variables(segmentId, FWH_TIME::surfMY, tau) = surfVY;
1120 m_surfaceData.variables(segmentId, FWH_TIME::surfMZ, tau) = surfVZ;
1129 up = m_surfaceData.variables(segmentId, FWH::U, tau) - uMean;
1130 vp = m_surfaceData.variables(segmentId, FWH::V, tau) - vMean;
1131 wp = m_surfaceData.variables(segmentId, FWH::W, tau) - wMean;
1132 pp = m_surfaceData.variables(segmentId, FWH::P, tau) - pMean;
1133 rho = m_surfaceData.variables(segmentId, FWH::RHO, tau);
1135 up = m_surfaceData.variables(segmentId, FWH_APE::UP, tau);
1136 vp = m_surfaceData.variables(segmentId, FWH_APE::VP, tau);
1137 wp = m_surfaceData.variables(segmentId, FWH_APE::WP, tau);
1138 pp = m_surfaceData.variables(segmentId, FWH_APE::PP, tau);
1143 const MFloat un = up * nx + vp * ny + wp * nz;
1144 const MFloat vn = surfVX * nx + surfVY * ny + surfVZ * nz;
1145 const MFloat deltaUn = un - vn;
1147 m_surfaceData.variables(segmentId, FWH_TIME::srcUn, tau) = vn + rho / rho_inf * deltaUn;
1148 m_surfaceData.variables(segmentId, FWH_TIME::srcLX, tau) = pp * nx + rho * up * deltaUn;
1149 m_surfaceData.variables(segmentId, FWH_TIME::srcLY, tau) = pp * ny + rho * vp * deltaUn;
1150 m_surfaceData.variables(segmentId, FWH_TIME::srcLZ, tau) = pp * nz + rho * wp * deltaUn;
1153 transformCoordinatesToFlowDirection3D(&m_surfaceData.variables(segmentId, FWH_TIME::surfMX, tau),
1154 &m_surfaceData.variables(segmentId, FWH_TIME::surfMY, tau),
1155 &m_surfaceData.variables(segmentId, FWH_TIME::surfMZ, tau));
1156 transformCoordinatesToFlowDirection3D(&m_surfaceData.variables(segmentId, FWH_TIME::srcLX, tau),
1157 &m_surfaceData.variables(segmentId, FWH_TIME::srcLY, tau),
1158 &m_surfaceData.variables(segmentId, FWH_TIME::srcLZ, tau));
1188 }
else if constexpr(nDim == 2) {
1189 TERMM(1,
"Time Domain FW-H not implimented in 2D: Requires free space Green function in 2D");
1199 switch(m_windowType) {
1200 case WINDOW::NONE: {
1201 m_log <<
" - using no window function" << std::endl;
1204 case WINDOW::HANNING: {
1205 m_log <<
" - using Hanning window function" << std::endl;
1207 for(
MInt t = 0; t < noSamples(); t++) {
1208 const MFloat w = 0.5 * (1.0 - cos(2.0 * PI * t / N));
1212 normFactor = 1.0 / sqrt(sum_w_sq / N);
1215 case WINDOW::HAMMING: {
1216 m_log <<
" - using Hamming window function" << std::endl;
1218 for(
MInt t = 0; t < noSamples(); t++) {
1219 const MFloat w = 0.54 - 0.46 * cos(2.0 * PI * t / N);
1223 normFactor = 1.0 / sqrt(sum_w_sq / N);
1226 case WINDOW::MODHANNING: {
1227 m_log <<
" - using modified-Hanning window function" << std::endl;
1229 for(
MInt t = 0; t < noSamples(); t++) {
1231 if(t < N / 8.0 || t > 7.0 * N / 8.0) {
1232 w = 0.5 * (1.0 - cos(8.0 * PI * t / N));
1239 normFactor = 1.0 / sqrt(sum_w_sq / N);
1243 TERMM(1,
"Invalid window type: '" + std::to_string(m_windowType) +
"'");
1247 m_log <<
" - normalization factor: " << normFactor << std::endl;
1248 m_windowNormFactor = normFactor;
1255 RECORD_TIMER_START(m_timers[Timers::WindowAndTransformSources]);
1256 printMessage(
"- Window and transform source terms");
1259 std::vector<MFloat> window(noSamples());
1260 std::fill(window.begin(), window.end(), 1.0);
1261 calcWindowFactors(window.data());
1264 std::vector<MInt> VarDim;
1265 std::vector<MInt> VarDimC;
1267 if constexpr(nDim == 3) {
1268 VarDim = {FWH::FX, FWH::FY, FWH::FZ, FWH::Q};
1269 VarDimC = {FWH::FX_C, FWH::FY_C, FWH::FZ_C, FWH::Q_C};
1271 VarDim = {FWH::FX, FWH::FY, FWH::Q};
1272 VarDimC = {FWH::FX_C, FWH::FY_C, FWH::Q_C};
1275 if constexpr(nDim == 3) {
1276 VarDim = {FWH_APE::FX, FWH_APE::FY, FWH_APE::FZ, FWH_APE::Q};
1277 VarDimC = {FWH_APE::FX_C, FWH_APE::FY_C, FWH_APE::FZ_C, FWH_APE::Q_C};
1279 VarDim = {FWH_APE::FX, FWH_APE::FY, FWH_APE::Q};
1280 VarDimC = {FWH_APE::FX_C, FWH_APE::FY_C, FWH_APE::Q_C};
1283 TERMM(1,
"Unknown acoustic method.");
1286 MInt noSourceTerms = 0;
1287 if constexpr(nDim == 3) {
1289 }
else if constexpr(nDim == 2) {
1293 const MFloat noSamplesF =
static_cast<MFloat>(noSamples());
1294 for(
MInt segmentId = 0; segmentId < totalNoSurfaceElements(); segmentId++) {
1295 for(
MInt i = 0; i < noSourceTerms; i++) {
1298 for(
MInt t = 0; t < noSamples(); t++) {
1299 sumSource += m_surfaceData.variables(segmentId, VarDim[i], t);
1301 const MFloat avgSource = sumSource / noSamplesF;
1305 for(
MInt t = 0; t < noSamples(); t++) {
1306 m_surfaceData.variables(segmentId, VarDim[i], t) -= avgSource;
1307 m_surfaceData.variables(segmentId, VarDim[i], t) *= window[t];
1312 for(
MInt i = 0; i < noSourceTerms; i++) {
1313 for(
MInt t = 0; t < noSamples(); t++) {
1314 m_surfaceData.complexVariables(segmentId, VarDimC[i], t, 0) =
1315 m_surfaceData.variables(segmentId, VarDim[i], t);
1316 m_surfaceData.complexVariables(segmentId, VarDimC[i], t, 1) = 0.0;
1321 checkNoSamplesPotencyOfTwo();
1322 if(m_FastFourier ==
true && m_transformationType == 1) {
1323 for(
MInt i = 0; i < noSourceTerms; i++) {
1324 FastFourierTransform(&m_surfaceData.complexVariables(segmentId, VarDimC[i]), noSamples(), 1,
nullptr,
true);
1326 }
else if(m_FastFourier ==
false || (m_FastFourier ==
true && m_transformationType == 0)) {
1327 for(
MInt i = 0; i < noSourceTerms; i++) {
1329 DiscreteFourierTransform(&m_surfaceData.complexVariables(segmentId, VarDimC[i]), noSamples(), 1,
nullptr,
true);
1332 TERMM(1,
"Error Fourier-Transform: Check transformationType and noSamples.");
1336 for(
MInt t = 0; t < noSamples(); t++) {
1337 for(
MInt i = 0; i < noSourceTerms; i++) {
1338 m_surfaceData.complexVariables(segmentId, VarDimC[i], t, 0) *= m_windowNormFactor;
1339 m_surfaceData.complexVariables(segmentId, VarDimC[i], t, 1) *= m_windowNormFactor;
1349 RECORD_TIMER_STOP(m_timers[Timers::WindowAndTransformSources]);
1358 std::vector<MFloat> window(noSamples());
1359 std::fill(window.begin(), window.end(), 1.0);
1360 calcWindowFactors(window.data());
1362 const MFloat noSamplesF =
static_cast<MFloat>(noSamples());
1363 for(
MInt observerId = 0; observerId < noObservers(); observerId++) {
1365 MFloat sumObsPressure = 0.0;
1366 for(
MInt t = 0; t < noSamples(); t++) {
1367 sumObsPressure += m_observerData.variables(observerId, 0, t);
1369 const MFloat aveObsPressure = sumObsPressure / noSamplesF;
1374 for(
MInt t = 0; t < noSamples(); t++) {
1376 m_observerData.complexVariables(observerId, 0, t, 0) =
1377 window[t] * (m_observerData.variables(observerId, 0, t) - aveObsPressure);
1378 m_observerData.complexVariables(observerId, 0, t, 1) = 0.0;
1382 checkNoSamplesPotencyOfTwo();
1383 if(m_FastFourier ==
true && m_transformationType == 1) {
1384 FastFourierTransform(&m_observerData.complexVariables(observerId, 0), noSamples(), 1,
nullptr,
true);
1385 }
else if(m_FastFourier ==
false || (m_FastFourier ==
true && m_transformationType == 0)) {
1387 DiscreteFourierTransform(&m_observerData.complexVariables(observerId, 0), noSamples(), 1,
nullptr,
true);
1389 TERMM(1,
"Error Fourier-Transform: Check transformationType and noSamples.");
1393 for(
MInt t = 0; t < noSamples(); t++) {
1394 m_observerData.complexVariables(observerId, 0, t, 0) *= m_windowNormFactor;
1395 m_observerData.complexVariables(observerId, 0, t, 1) *= m_windowNormFactor;
1412 const MBool inPlace) {
1414 TERMM_IF_NOT_COND(dir == 1 || dir == -1,
"Invalid direction");
1415 TERMM_IF_NOT_COND(size % 2 == 0,
"FFT: not a multiple of 2 samples.");
1417 const MFloat isign = (dir == 1) ? -1.0 : 1.0;
1418 const MBool dim = (dir == 1) ?
true :
false;
1419 const MInt size2 = 2 * size;
1422 MFloat*
const data = (inPlace) ? dataArray : result;
1425 ASSERT(result !=
nullptr,
"Pointer to result storage is nullptr.");
1426 std::copy_n(&dataArray[0], size2, result);
1435 const MInt nn = size;
1440 for(
MInt i = 1; i < n; i += 2) {
1442 SWAP(data[j - 1], data[i - 1]);
1443 SWAP(data[j], data[i]);
1446 while(m >= 2 && j > m) {
1455 MFloat wtemp, wr, wpr, wpi, wi, theta, tempr, tempi;
1458 while(nLong > mmax) {
1460 theta = isign * (6.28318530717959 / mmax);
1461 wtemp = sin(0.5 * theta);
1462 wpr = -2.0 * wtemp * wtemp;
1466 for(p = 1; p < mmax; p += 2) {
1467 for(i = p; i <= nLong; i += istep) {
1469 tempr = wr * data[k - 1] - wi * data[k];
1470 tempi = wr * data[k] + wi * data[k - 1];
1471 data[k - 1] = data[i - 1] - tempr;
1472 data[k] = data[i] - tempi;
1473 data[i - 1] += tempr;
1476 wr = (wtemp = wr) * wpr - wi * wpi + wr;
1477 wi = wi * wpr + wtemp * wpi + wi;
1484 for(
MInt z = 0; z < size2; z++) {
1501 const MBool inPlace) {
1504 const MInt size2 = 2 * size;
1505 MFloat* resultPtr =
nullptr;
1506 std::vector<MFloat> resultArray;
1509 resultArray.resize(size2);
1510 resultPtr = &resultArray[0];
1512 ASSERT(result !=
nullptr,
"Pointer to result storage is nullptr.");
1517 const MFloat con = 2.0 * PI / sizeF;
1518 for(
MInt k = 0; k < size; k++) {
1519 for(
MInt i = 0; i < size; i++) {
1522 resultPtr[2 * k] += dataArray[2 * i] * cos(k * con * i) + dataArray[2 * i + 1] * sin(k * con * i);
1523 resultPtr[2 * k + 1] += -1.0 * dataArray[2 * i] * sin(k * con * i) + dataArray[2 * i + 1] * cos(k * con * i);
1525 resultPtr[2 * k] /= sizeF;
1526 resultPtr[2 * k + 1] /= sizeF;
1528 }
else if(dir == -1) {
1529 for(
MInt t = 0; t < size; t++) {
1530 const MFloat arg = 2.0 * PI * t / sizeF;
1531 for(
MInt k = 0; k < size; k++) {
1532 resultPtr[2 * t] += dataArray[2 * k] * cos(arg * k) - dataArray[2 * k + 1] * sin(arg * k);
1533 resultPtr[2 * t + 1] += dataArray[2 * k] * sin(arg * k) + dataArray[2 * k + 1] * cos(arg * k);
1537 TERMM(1,
"Please insert 1 for forward or -1 for backward");
1542 std::copy_n(&resultPtr[0], size2, &dataArray[0]);
1551 printMessage(
"- Calculate frequencies");
1555 for(
MInt i = 0, k = noSamples() - 1; i <= noSamples() / 2; k--, i++) {
1556 if(k > noSamples() / 2) {
1557 m_frequencies[k] = -1.0 *
static_cast<MFloat>(i + 1) / (noSamples() * m_dt);
1559 m_frequencies[i] =
static_cast<MFloat>(i) / (noSamples() * m_dt);
1562 for(
MInt i = 0; i < noSamples(); i++) {
1563 m_frequencies[i] *= 2.0 * PI;
1574 MFloat*
const p_complexVariables) {
1575 RECORD_TIMER_START(m_timers[Timers::CalcSurfaceIntegrals]);
1576 std::vector<MInt> VarDimC;
1578 if constexpr(nDim == 3) {
1579 VarDimC = {FWH::FX_C, FWH::FY_C, FWH::FZ_C, FWH::Q_C};
1581 VarDimC = {FWH::FX_C, FWH::FY_C, FWH::Q_C};
1584 if constexpr(nDim == 3) {
1585 VarDimC = {FWH_APE::FX_C, FWH_APE::FY_C, FWH_APE::FZ_C, FWH_APE::Q_C};
1587 VarDimC = {FWH_APE::FX_C, FWH_APE::FY_C, FWH_APE::Q_C};
1590 TERMM(1,
"Unknown acoustic method.");
1594 const MFloat mach = sqrt(std::inner_product(&m_MaVec[0], &m_MaVec[nDim], &m_MaVec[0], 0.0));
1596 const MFloat betaSq = 1.0 - mach * mach;
1597 const MFloat beta = sqrt(betaSq);
1598 const MFloat fbeta2 = 1 / betaSq;
1600 if constexpr(nDim == 2) {
1601 const MFloat Max = m_MaVec[0];
1602 const MFloat May = m_MaVec[1];
1605 if(!
approx(Max, 0.0, MFloatEps) && !
approx(May, 0.0, MFloatEps)) {
1606 theta = atan2(May, Max);
1610 const MFloat sinTheta = sin(theta);
1611 const MFloat cosTheta = cos(theta);
1616 maia::parallelFor<false>(1, noSamples() / 2 + 1, [=](
MInt nw) {
1618 const MFloat waveNumber = m_frequencies[nw];
1621 MFloat integralFxRe = 0.0;
1622 MFloat integralFxIm = 0.0;
1623 MFloat integralFyRe = 0.0;
1624 MFloat integralFyIm = 0.0;
1625 MFloat integralQRe = 0.0;
1626 MFloat integralQIm = 0.0;
1629 for(
MInt segmentId = 0; segmentId < totalNoSurfaceElements(); segmentId++) {
1632 const MFloat*
const surfCoordinates = &m_surfaceData.surfaceCoordinates(segmentId);
1633 const MFloat surfX = surfCoordinates[0];
1634 const MFloat surfY = surfCoordinates[1];
1635 const MFloat X = (coord[0] - surfX) * cosTheta + (coord[1] - surfY) * sinTheta;
1636 const MFloat Y = -1.0 * (coord[0] - surfX) * sinTheta + (coord[1] - surfY) * cosTheta;
1638 const MFloat d = sqrt(X * X + betaSq * Y * Y);
1642 const MFloat argH = waveNumber * fbeta2 * d;
1643 const MFloat dargHdxi = waveNumber * fbeta2 * fd * (-1.0 * X * cosTheta + betaSq * Y * sinTheta);
1644 const MFloat dargHdeta = waveNumber * fbeta2 * fd * (-1.0 * X * sinTheta - betaSq * Y * cosTheta);
1647 const MFloat argExp = mach * waveNumber * X * fbeta2;
1648 const MFloat dargEdxi = -1.0 * mach * waveNumber * cosTheta * fbeta2;
1649 const MFloat dargEdeta = -1.0 * mach * waveNumber * sinTheta * fbeta2;
1650 const MFloat C1 = 1.0 / (4.0 * beta);
1652 const MFloat J0 = j0(argH);
1653 const MFloat J1 = j1(argH);
1654 const MFloat Y0 = y0(argH);
1655 const MFloat Y1 = y1(argH);
1657 const MFloat cosargE = cos(argExp);
1658 const MFloat sinargE = sin(argExp);
1661 const MFloat greenValue_Re = C1 * (cosargE * Y0 - sinargE * J0);
1662 const MFloat greenValue_Im = C1 * (cosargE * J0 + sinargE * Y0);
1665 const MFloat dGreendXi_Re =
1666 C1 * (dargEdxi * (-1.0 * cosargE * J0 - sinargE * Y0) + dargHdxi * (-1.0 * cosargE * Y1 + sinargE * J1));
1667 const MFloat dGreendXi_Im =
1668 C1 * (dargEdxi * (cosargE * Y0 - sinargE * J0) + dargHdxi * (-1.0 * cosargE * J1 - sinargE * Y1));
1669 const MFloat dGreendEta_Re =
1670 C1 * (dargEdeta * (-1.0 * cosargE * J0 - sinargE * Y0) + dargHdeta * (-1.0 * cosargE * Y1 + sinargE * J1));
1671 const MFloat dGreendEta_Im =
1672 C1 * (dargEdeta * (cosargE * Y0 - sinargE * J0) + dargHdeta * (-1.0 * cosargE * J1 - sinargE * Y1));
1675 const MFloat dL = m_surfaceData.surfaceArea(segmentId);
1677 const MFloat FxRe = m_surfaceData.complexVariables(segmentId, VarDimC[0], nw, 0);
1678 const MFloat FxIm = m_surfaceData.complexVariables(segmentId, VarDimC[0], nw, 1);
1680 const MFloat FyRe = m_surfaceData.complexVariables(segmentId, VarDimC[1], nw, 0);
1681 const MFloat FyIm = m_surfaceData.complexVariables(segmentId, VarDimC[1], nw, 1);
1683 const MFloat QRe = m_surfaceData.complexVariables(segmentId, VarDimC[2], nw, 0);
1684 const MFloat QIm = m_surfaceData.complexVariables(segmentId, VarDimC[2], nw, 1);
1687 integralFxRe += dL * (FxRe * dGreendXi_Re - FxIm * dGreendXi_Im);
1688 integralFxIm += dL * (FxRe * dGreendXi_Im + FxIm * dGreendXi_Re);
1690 integralFyRe += dL * (FyRe * dGreendEta_Re - FyIm * dGreendEta_Im);
1691 integralFyIm += dL * (FyRe * dGreendEta_Im + FyIm * dGreendEta_Re);
1693 integralQRe += -1.0 * waveNumber * dL * (QRe * greenValue_Im + QIm * greenValue_Re);
1694 integralQIm += waveNumber * dL * (QRe * greenValue_Re - QIm * greenValue_Im);
1698 const MFloat integralRe = -1.0 * (integralFxRe + integralFyRe + integralQRe);
1699 const MFloat integralIm = -1.0 * (integralFxIm + integralFyIm + integralQIm);
1702 p_complexVariables[2 * nw + 0] = integralRe;
1703 p_complexVariables[2 * nw + 1] = integralIm;
1705 }
else if constexpr(nDim == 3) {
1706 const MFloat Max = m_MaVec[0];
1707 const MFloat May = m_MaVec[1];
1708 const MFloat Maz = m_MaVec[2];
1715 if(!
approx(Maz, 0.0, MFloatEps) && !
approx(Max, 0.0, MFloatEps)) {
1716 theta = atan2(Maz, Max);
1718 if(!
approx(May, 0.0, MFloatEps)) {
1719 alpha = asin(May / mach);
1723 const MFloat sinAlpha = sin(alpha);
1724 const MFloat cosAlpha = cos(alpha);
1725 const MFloat sinTheta = sin(theta);
1726 const MFloat cosTheta = cos(theta);
1729 maia::parallelFor<false>(1, noSamples() / 2 + 1, [=](
MInt nw) {
1731 const MFloat waveNumber = m_frequencies[nw];
1734 MFloat integralFxRe = 0.0;
1735 MFloat integralFxIm = 0.0;
1736 MFloat integralFyRe = 0.0;
1737 MFloat integralFyIm = 0.0;
1738 MFloat integralFzRe = 0.0;
1739 MFloat integralFzIm = 0.0;
1740 MFloat integralQRe = 0.0;
1741 MFloat integralQIm = 0.0;
1744 for(
MInt segmentId = 0; segmentId < totalNoSurfaceElements(); segmentId++) {
1745 MFloat* surfCoordinates = &m_surfaceData.surfaceCoordinates(segmentId);
1747 const MFloat x = coord[0];
1749 const MFloat z = coord[2];
1750 const MFloat xi = surfCoordinates[0];
1751 const MFloat eta = surfCoordinates[1];
1752 const MFloat zeta = surfCoordinates[2];
1780 const MFloat X = (x - xi) * cosAlpha * cosTheta + (
y - eta) * sinAlpha + (z - zeta) * cosAlpha * sinTheta;
1782 const MFloat Y = -(x - xi) * sinAlpha * cosTheta + (
y - eta) * cosAlpha - (z - zeta) * sinAlpha * sinTheta;
1783 const MFloat Z = -(x - xi) * sinTheta + (z - zeta) * cosTheta;
1784 const MFloat d = std::sqrt(X * X + betaSq * (Y * Y + Z * Z));
1785 const MFloat k = waveNumber;
1788 const MFloat F1B4pid = 1.0 / (4.0 * PI * d);
1789 const MFloat d_xi = (-X * cosAlpha * cosTheta + betaSq * (Y * sinAlpha * cosTheta + Z * sinTheta)) / d;
1790 const MFloat d_eta = (-X * sinAlpha - betaSq * Y * cosAlpha) / d;
1791 const MFloat d_zeta = (-X * cosAlpha * sinTheta + betaSq * (-Y * sinAlpha * sinTheta - Z * cosTheta)) / d;
1793 const MFloat arg = k * (mach * X - d) / betaSq;
1794 const MFloat cosArg = cos(arg);
1795 const MFloat sinArg = sin(arg);
1798 const MFloat P0 = F1B4pid * mach * k / betaSq;
1799 const MFloat P1 = F1B4pid / d;
1800 const MFloat P2 = F1B4pid * k / betaSq;
1803 const MFloat greenValue_Re = - F1B4pid * cosArg;
1804 const MFloat greenValue_Im = - F1B4pid * sinArg;
1805 const MFloat dGreendXi_Re =-P0 * sinArg * cosAlpha * cosTheta + P1 * d_xi * cosArg - P2 * d_xi * sinArg;
1806 const MFloat dGreendXi_Im = P0 * cosArg * cosAlpha * cosTheta + P1 * d_xi * sinArg + P2 * d_xi * cosArg;
1807 const MFloat dGreendEta_Re =-P0 * sinArg * sinAlpha + P1 * d_eta * cosArg - P2 * d_eta * sinArg;
1808 const MFloat dGreendEta_Im = P0 * cosArg * sinAlpha + P1 * d_eta * sinArg + P2 * d_eta * cosArg;
1809 const MFloat dGreendZeta_Re =-P0 * sinArg * cosAlpha * sinTheta + P1 * d_zeta * cosArg - P2 * d_zeta * sinArg;
1810 const MFloat dGreendZeta_Im = P0 * cosArg * cosAlpha * sinTheta + P1 * d_zeta * sinArg + P2 * d_zeta * cosArg;
1813 const MFloat dL = m_surfaceData.surfaceArea(segmentId);
1815 const MFloat FxRe = m_surfaceData.complexVariables(segmentId, VarDimC[0], nw, 0);
1816 const MFloat FxIm = m_surfaceData.complexVariables(segmentId, VarDimC[0], nw, 1);
1818 const MFloat FyRe = m_surfaceData.complexVariables(segmentId, VarDimC[1], nw, 0);
1819 const MFloat FyIm = m_surfaceData.complexVariables(segmentId, VarDimC[1], nw, 1);
1821 const MFloat FzRe = m_surfaceData.complexVariables(segmentId, VarDimC[2], nw, 0);
1822 const MFloat FzIm = m_surfaceData.complexVariables(segmentId, VarDimC[2], nw, 1);
1824 const MFloat QRe = m_surfaceData.complexVariables(segmentId, VarDimC[3], nw, 0);
1825 const MFloat QIm = m_surfaceData.complexVariables(segmentId, VarDimC[3], nw, 1);
1828 integralFxRe += dL * (FxRe * dGreendXi_Re - FxIm * dGreendXi_Im);
1829 integralFxIm += dL * (FxRe * dGreendXi_Im + FxIm * dGreendXi_Re);
1831 integralFyRe += dL * (FyRe * dGreendEta_Re - FyIm * dGreendEta_Im);
1832 integralFyIm += dL * (FyRe * dGreendEta_Im + FyIm * dGreendEta_Re);
1834 integralFzRe += dL * (FzRe * dGreendZeta_Re - FzIm * dGreendZeta_Im);
1835 integralFzIm += dL * (FzRe * dGreendZeta_Im + FzIm * dGreendZeta_Re);
1837 integralQRe += -1.0 * waveNumber * dL * (QRe * greenValue_Im + QIm * greenValue_Re);
1838 integralQIm += waveNumber * dL * (QRe * greenValue_Re - QIm * greenValue_Im);
1842 const MFloat integralRe = -1.0 * (integralFxRe + integralFyRe + integralFzRe + integralQRe);
1843 const MFloat integralIm = -1.0 * (integralFxIm + integralFyIm + integralFzIm + integralQIm);
1847 p_complexVariables[2 * nw + 0] = integralRe;
1848 p_complexVariables[2 * nw + 1] = integralIm;
1853 p_complexVariables[2 * nw + 0] = 0.0;
1854 p_complexVariables[2 * nw + 1] = 0.0;
1855 RECORD_TIMER_STOP(m_timers[Timers::CalcSurfaceIntegrals]);
1868 const std::array<MFloat, nDim> coord = getGlobalObserverCoordinates(globalObserverId);
1871 const MFloat mach = sqrt(std::inner_product(&m_MaVec[0], &m_MaVec[nDim], &m_MaVec[0], 0.0));
1872 const MFloat betaSq = 1.0 - mach * mach;
1875 maia::parallelFor<false>(0, noSamples(), [=](
MInt tau) {
1876 if constexpr(nDim == 3) {
1878 for(
MInt segmentId = 0; segmentId < totalNoSurfaceElements(); segmentId++) {
1879 MFloat* surfCoordinates = &m_surfaceData.surfaceCoordinates(segmentId);
1882 MFloat distX = coord[0] - surfCoordinates[0];
1883 MFloat distY = coord[1] - surfCoordinates[1];
1884 MFloat distZ = coord[2] - surfCoordinates[2];
1885 transformCoordinatesToFlowDirection3D(&distX, &distY, &distZ);
1889 const MFloat R = sqrt(distX * distX + betaSq * (distY * distY + distZ * distZ));
1890 const MFloat dist = (R - mach * distX) / betaSq;
1893 const MFloat invsersDistSq = invsersDist * invsersDist;
1894 const MFloat distUnitX = (distX - mach * R) * invsersDist / betaSq;
1895 const MFloat distUnitY = distY * invsersDist;
1896 const MFloat distUnitZ = distZ * invsersDist;
1899 const MFloat timeSource = tau * m_dt;
1900 const MFloat timeObserver = timeSource +
dist;
1903 const MFloat Mx = m_surfaceData.variables(segmentId, FWH_TIME::surfMX, tau);
1904 const MFloat My = m_surfaceData.variables(segmentId, FWH_TIME::surfMY, tau);
1905 const MFloat Mz = m_surfaceData.variables(segmentId, FWH_TIME::surfMZ, tau);
1906 const MFloat srcUn = m_surfaceData.variables(segmentId, FWH_TIME::srcUn, tau);
1907 const MFloat srcLX = m_surfaceData.variables(segmentId, FWH_TIME::srcLX, tau);
1908 const MFloat srcLY = m_surfaceData.variables(segmentId, FWH_TIME::srcLY, tau);
1909 const MFloat srcLZ = m_surfaceData.variables(segmentId, FWH_TIME::srcLZ, tau);
1912 const MFloat Mr = distUnitX * Mx + distUnitY * My + distUnitZ * Mz;
1913 const MFloat inverseDopplerFactor = 1.0 / (1.0 - Mr);
1914 const MFloat inverseDopplerFactorSq = inverseDopplerFactor * inverseDopplerFactor;
1915 const MFloat Lr = distUnitX * srcLX + distUnitY * srcLY + distUnitZ * srcLZ;
1916 const MFloat LM = srcLX * Mx + srcLY * My + srcLZ * Mz;
1919 const MFloat dotMX = calcTimeDerivative(segmentId, FWH_TIME::surfMX, tau);
1920 const MFloat dotMY = calcTimeDerivative(segmentId, FWH_TIME::surfMY, tau);
1921 const MFloat dotMZ = calcTimeDerivative(segmentId, FWH_TIME::surfMZ, tau);
1922 const MFloat dotUn = calcTimeDerivative(segmentId, FWH_TIME::srcUn, tau);
1923 const MFloat dotLX = calcTimeDerivative(segmentId, FWH_TIME::srcLX, tau);
1924 const MFloat dotLY = calcTimeDerivative(segmentId, FWH_TIME::srcLY, tau);
1925 const MFloat dotLZ = calcTimeDerivative(segmentId, FWH_TIME::srcLZ, tau);
1928 const MFloat dotMr = distUnitX * dotMX + distUnitY * dotMY + distUnitZ * dotMZ;
1929 const MFloat dotLr = distUnitX * dotLX + distUnitY * dotLY + distUnitZ * dotLZ;
1931 const MFloat MrFactor0 = invsersDist * inverseDopplerFactorSq;
1933 (
dist * dotMr + Mr - mach * mach) * invsersDistSq * inverseDopplerFactor * inverseDopplerFactorSq;
1936 const MFloat surfIntegralPPT_0 = dotUn * MrFactor0;
1937 const MFloat surfIntegralPPT_1 = srcUn * MrFactor1;
1940 const MFloat surfIntegralPPL_0 = dotLr * MrFactor0;
1941 const MFloat surfIntegralPPL_1 = (Lr - LM) * MrFactor0 * invsersDist;
1942 const MFloat surfIntegralPPL_2 = Lr * MrFactor1;
1945 m_surfaceData.variables(segmentId, FWH_TIME::timeObs, tau) = timeObserver;
1946 m_surfaceData.variables(segmentId, FWH_TIME::fwhPP, tau) =
1947 surfIntegralPPT_0 + surfIntegralPPT_1 + surfIntegralPPL_0 + surfIntegralPPL_1 + surfIntegralPPL_2;
1948 m_surfaceData.variables(segmentId, FWH_TIME::fwhPP, tau) *= m_surfaceData.surfaceArea(segmentId);
1987 }
else if constexpr(nDim == 2) {
1988 TERMM(1,
"Time Domain FW-H not implimented in 2D: Requires free space Green function in 2D");
1995 if(m_zeroFlowAngle)
return;
1998 const MFloat fX_old = *fX;
1999 const MFloat fY_old = *fY;
2001 *fX = fX_old * m_transformationMatrix[0] + fY_old * m_transformationMatrix[1];
2002 *fY = fX_old * m_transformationMatrix[2] + fY_old * m_transformationMatrix[3];
2007 if(m_zeroFlowAngle)
return;
2010 const MFloat fX_old = *fX;
2011 const MFloat fY_old = *fY;
2012 const MFloat fZ_old = *fZ;
2014 *fX = fX_old * m_transformationMatrix[0] + fY_old * m_transformationMatrix[1] + fZ_old * m_transformationMatrix[2];
2015 *fY = fX_old * m_transformationMatrix[3] + fY_old * m_transformationMatrix[4] + fZ_old * m_transformationMatrix[5];
2016 *fZ = fX_old * m_transformationMatrix[6] + fY_old * m_transformationMatrix[7] + fZ_old * m_transformationMatrix[8];
2021 if(m_zeroFlowAngle)
return;
2025 const MFloat fX_old = *fX;
2026 const MFloat fY_old = *fY;
2028 *fX = fX_old * m_transformationMatrix[0] - fY_old * m_transformationMatrix[1];
2029 *fY = -fX_old * m_transformationMatrix[2] + fY_old * m_transformationMatrix[3];
2034 if(m_zeroFlowAngle)
return;
2038 const MFloat fX_old = *fX;
2039 const MFloat fY_old = *fY;
2040 const MFloat fZ_old = *fZ;
2042 *fX = fX_old * m_transformationMatrix[0] - fY_old * m_transformationMatrix[1] - fZ_old * m_transformationMatrix[2];
2043 *fY = -fX_old * m_transformationMatrix[3] + fY_old * m_transformationMatrix[4] + fZ_old * m_transformationMatrix[5];
2044 *fZ = -fX_old * m_transformationMatrix[6] + fY_old * m_transformationMatrix[7] + fZ_old * m_transformationMatrix[8];
2053 dotVar = -1.0 * m_surfaceData.variables(segmentId, varId, tau + 2)
2054 + 4.0 * m_surfaceData.variables(segmentId, varId, tau + 1)
2055 - 3.0 * m_surfaceData.variables(segmentId, varId, tau);
2056 }
else if(tau == noSamples() - 1) {
2057 dotVar = +3.0 * m_surfaceData.variables(segmentId, varId, tau)
2058 - 4.0 * m_surfaceData.variables(segmentId, varId, tau - 1)
2059 + 1.0 * m_surfaceData.variables(segmentId, varId, tau - 2);
2061 dotVar = +1.0 * m_surfaceData.variables(segmentId, varId, tau + 1)
2062 - 1.0 * m_surfaceData.variables(segmentId, varId, tau - 1);
2064 return (dotVar / 2.0 / m_dt);
2070 if(m_fwhTimeShiftType == 0)
return;
2073 const MFloat mach = sqrt(std::inner_product(&m_MaVec[0], &m_MaVec[nDim], &m_MaVec[0], 0.0));
2074 const MFloat betaSq = 1.0 - mach * mach;
2076 printMessage(
"- Loop over observers to find minimum distance from the source to the observers");
2077 for(
MInt k = 0; k < globalNoObservers(); k++) {
2078 MFloat distMinLocal = std::numeric_limits<MFloat>::max();
2081 const std::array<MFloat, nDim> coord = getGlobalObserverCoordinates(k);
2084 for(
MInt segmentId = 0; segmentId < totalNoSurfaceElements(); segmentId++) {
2085 MFloat* surfCoordinates = &m_surfaceData.surfaceCoordinates(segmentId);
2087 if constexpr(nDim == 3) {
2089 MFloat distX = coord[0] - surfCoordinates[0];
2090 MFloat distY = coord[1] - surfCoordinates[1];
2091 MFloat distZ = coord[2] - surfCoordinates[2];
2092 transformCoordinatesToFlowDirection3D(&distX, &distY, &distZ);
2095 const MFloat R = sqrt(distX * distX + betaSq * (distY * distY + distZ * distZ));
2096 const MFloat dist = (R - mach * distX) / betaSq;
2099 distMinLocal = (
dist < distMinLocal) ?
dist : distMinLocal;
2100 }
else if constexpr(nDim == 2) {
2101 TERMM(1,
"Time Domain FW-H not implimented in 2D: Requires free space Green function in 2D");
2105 distMinObservers[k] = distMinLocal;
2109 mpiComm(), AT_,
"MPI_IN_PLACE",
"distMinObservers");
2111 if(m_fwhTimeShiftType == 2) {
2112 const MFloat disMinGlobal = *std::min_element(distMinObservers, distMinObservers + globalNoObservers());
2113 std::fill(distMinObservers, distMinObservers + globalNoObservers(), disMinGlobal);
2114 if(domainId() == 0) {
2115 std::cout <<
"Apply universal time shift for all observers = " << disMinGlobal << std::endl;
2124 const MFloat inverseFourPI = 1.0 / 4.0 / PI;
2127 maia::parallelFor<false>(0, noSamples(), [=](
MInt t) {
2129 MFloat timeObserver = t * m_dt + distMinObservers;
2135 for(
MInt segmentId = 0; segmentId < totalNoSurfaceElements(); segmentId++) {
2137 MFloat interpolateFwhPP = 0.0;
2139 MInt t_upper = noSamples() - 1;
2142 MFloat timeObserver_lower = m_surfaceData.variables(segmentId, FWH_TIME::timeObs, t_lower);
2143 MFloat timeObserver_upper = m_surfaceData.variables(segmentId, FWH_TIME::timeObs, t_upper);
2146 if(timeObserver < timeObserver_lower || timeObserver > timeObserver_upper) {
2148 interpolateFwhPP = 0.0;
2149 }
else if(
approx(timeObserver, timeObserver_lower, MFloatEps)) {
2151 interpolateFwhPP = m_surfaceData.variables(segmentId, FWH_TIME::fwhPP, t_lower);
2152 }
else if(
approx(timeObserver, timeObserver_upper, MFloatEps)) {
2154 interpolateFwhPP = m_surfaceData.variables(segmentId, FWH_TIME::fwhPP, t_upper);
2159 MInt t_range = t_upper - t_lower;
2162 const MFloat t_mid = (t_range % 2 == 0) ? ((t_lower + t_upper) / 2) : ((t_lower + t_upper - 1) / 2);
2163 const MFloat timeObserver_mid = m_surfaceData.variables(segmentId, FWH_TIME::timeObs, t_mid);
2165 if(
approx(timeObserver, timeObserver_mid, MFloatEps)) {
2167 interpolateFwhPP = m_surfaceData.variables(segmentId, FWH_TIME::fwhPP, t_mid);
2172 if(timeObserver < timeObserver_mid) {
2174 }
else if(timeObserver > timeObserver_mid) {
2177 t_range = t_upper - t_lower;
2181 timeObserver_lower = m_surfaceData.variables(segmentId, FWH_TIME::timeObs, t_lower);
2182 timeObserver_upper = m_surfaceData.variables(segmentId, FWH_TIME::timeObs, t_upper);
2183 const MFloat fwhPP_lower = m_surfaceData.variables(segmentId, FWH_TIME::fwhPP, t_lower);
2184 const MFloat fwhPP_upper = m_surfaceData.variables(segmentId, FWH_TIME::fwhPP, t_upper);
2185 const MFloat fwhPP_slop = (fwhPP_upper - fwhPP_lower) / (timeObserver_upper - timeObserver_lower);
2186 interpolateFwhPP = fwhPP_lower + (timeObserver - timeObserver_lower) * fwhPP_slop;
2189 }
while(t_range > 1);
2192 sumFwhPP += interpolateFwhPP;
2195 p_perturbedFWH[t] = inverseFourPI * sumFwhPP;
2205 RECORD_TIMER_START(m_timers[Timers::CombineSurfaceIntegrals]);
2214 constexpr MInt noRealAndIm = 2;
2215 const MInt halfNoSamplesRealAndIm = (noSamples() / 2 + 1) * noRealAndIm;
2216 const MInt dataBuffSize = halfNoSamplesRealAndIm * 1.0;
2217 std::vector<MFloat> obsDataBuff(dataBuffSize, 0.0);
2219 const MInt trgDomain = getObserverDomainId(globalObserverId);
2220 const MInt observerId = a_localObserverId(globalObserverId);
2222 TERMM_IF_COND(trgDomain == domainId() && (observerId < 0 || observerId > noObservers()),
2223 "Error in observer partitioning!");
2227 std::copy_n(p_complexVariables, halfNoSamplesRealAndIm, obsDataBuff.data());
2230 if(domainId() == trgDomain) {
2233 mpiComm(), AT_,
"MPI_IN_PLACE",
"obsDataBuff");
2238 std::copy_n(obsDataBuff.data(), halfNoSamplesRealAndIm, &m_observerData.complexVariables(observerId, 0));
2241 mpiComm(), AT_,
"MPI_IN_PLACE",
"obsDataBuff");
2248 RECORD_TIMER_STOP(m_timers[Timers::CombineSurfaceIntegrals]);
2260 const MInt dataBuffSize = noSamples();
2261 std::vector<MFloat> obsDataBuff(dataBuffSize, 0.0);
2263 const MInt trgDomain = getObserverDomainId(globalObserverId);
2264 const MInt observerId = a_localObserverId(globalObserverId);
2266 TERMM_IF_COND(trgDomain == domainId() && (observerId < 0 || observerId > noObservers()),
2267 "Error in observer partitioning!");
2271 std::copy_n(p_perturbedFWH, dataBuffSize, obsDataBuff.data());
2274 if(domainId() == trgDomain) {
2277 mpiComm(), AT_,
"MPI_IN_PLACE",
"obsDataBuff");
2281 std::copy_n(obsDataBuff.data(), dataBuffSize, &m_observerData.variables(observerId, 0));
2284 mpiComm(), AT_,
"MPI_IN_PLACE",
"obsDataBuff");
2295 printMessage(
"- Calculate observer signals in frequency domain");
2297 complexVariablesBuffer.
fill(0.0);
2298 MFloat*
const p_complexVariables = complexVariablesBuffer.
data();
2299 for(
MInt globalObserverId = 0; globalObserverId < globalNoObservers(); globalObserverId++) {
2300 std::array<MFloat, nDim> coord = getGlobalObserverCoordinates(globalObserverId);
2301 calcSurfaceIntegralsForObserver(coord, p_complexVariables);
2302 applySymmetryBc(coord, p_complexVariables);
2303 combineSurfaceIntegrals(globalObserverId, p_complexVariables);
2312 distMinBuffer.
fill(0.0);
2313 MFloat*
const distMinObservers = distMinBuffer.
data();
2317 perturbedPressureBuffer.
fill(0.0);
2318 MFloat*
const p_perturbedFWH = perturbedPressureBuffer.
data();
2320 printMessage(
"- Calculate observer signals in time domain");
2321 calcMinDistanceForObservers(distMinObservers);
2323 for(
MInt globalObserverId = 0; globalObserverId < globalNoObservers(); globalObserverId++) {
2325 calcSurfaceIntegralsAtSourceTime(globalObserverId);
2326 interpolateFromSourceToObserverTime(distMinObservers[globalObserverId], p_perturbedFWH);
2327 combineSurfaceIntegralsInTime(globalObserverId, p_perturbedFWH);
2335 const MInt minNoObsPerDomain = globalNoObservers() / noDomains();
2336 const MInt remainderObs = globalNoObservers() % noDomains();
2337 if(domainId() < remainderObs) {
2338 m_noObservers = minNoObsPerDomain + 1;
2339 m_offsetObserver = domainId() * (minNoObsPerDomain + 1);
2341 m_noObservers = minNoObsPerDomain;
2342 m_offsetObserver = domainId() * minNoObsPerDomain + remainderObs;
2350 RECORD_TIMER_START(m_timers[Timers::BacktransformObservers]);
2351 printMessage(
"- Backtransforming the observer signals");
2357 for(
MInt obs = 0; obs < noObservers(); obs++) {
2360 for(
MInt i = 1; i < noSamples() / 2; i++) {
2361 m_observerData.complexVariables(obs, 0, noSamples() - i, 0) = m_observerData.complexVariables(obs, 0, i, 0);
2362 m_observerData.complexVariables(obs, 0, noSamples() - i, 1) =
2363 -1.0 * m_observerData.complexVariables(obs, 0, i, 1);
2366 std::fill_n(&backtransform[0], 2 * noSamples(), 0.0);
2368 if(m_FastFourier ==
true && m_transformationType == 1) {
2369 FastFourierTransform(&m_observerData.complexVariables(obs, 0), noSamples(), -1, &backtransform[0],
false);
2370 }
else if(m_FastFourier ==
false || (m_FastFourier ==
true && m_transformationType == 0)) {
2371 DiscreteFourierTransform(&m_observerData.complexVariables(obs, 0), noSamples(), -1, &backtransform[0],
false);
2375 for(
MInt i = 0; i < noSamples(); i++) {
2376 m_observerData.variables(obs, 0, i) = backtransform[2 * i];
2380 RECORD_TIMER_STOP(m_timers[Timers::BacktransformObservers]);
2390 RECORD_TIMER_START(m_timers[Timers::SaveObserverSignals]);
2392 printMessage(
"- Save observer signals");
2395 const MString fileName = outputDir() + m_outputFilePrefix +
"observerData" + ParallelIo::fileExt();
2397 ParallelIo file(fileName, PIO_REPLACE, mpiComm());
2399 ParallelIo::size_type dimSizesCoordinates[] = {globalNoObservers(), nDim};
2400 file.defineArray(PIO_FLOAT,
"coordinates", 2, &dimSizesCoordinates[0]);
2402 file.defineArray(PIO_FLOAT,
"time", noSamples());
2403 file.defineArray(PIO_FLOAT,
"frequency", noSamples());
2405 ParallelIo::size_type dimSizesVarTime[] = {globalNoObservers(), noSamples()};
2406 file.defineArray(PIO_FLOAT,
"pressure_time", 2, &dimSizesVarTime[0]);
2408 ParallelIo::size_type dimSizesVarFreq[] = {globalNoObservers(), 2 * noSamples()};
2409 file.defineArray(PIO_FLOAT,
"pressure_frequency", 2, &dimSizesVarFreq[0]);
2412 const MString acousticMethod = (m_acousticMethod ==
FWH_METHOD) ?
"FWH" :
"FWH_APE";
2413 file.setAttribute(acousticMethod,
"acousticMethod");
2414 file.setAttribute(m_Ma,
"Ma");
2415 if(!
approx(m_Ma, m_MaDim, MFloatEps)) {
2416 file.setAttribute(m_MaDim,
"Ma_dim");
2418 file.setAttributes(m_MaVec,
"Ma_i", nDim);
2420 file.setAttribute(m_noSamples,
"noSamples");
2421 file.setAttribute(m_sampleStride,
"sampleStride");
2422 file.setAttribute(m_sampleOffset,
"sampleOffset");
2423 file.setAttribute(m_dt,
"dt");
2425 file.setAttribute(WINDOW::windowNames[m_windowType],
"windowType");
2426 file.setAttribute(m_windowNormFactor,
"windowNormFactor");
2428 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2429 file.setAttribute(m_surfaceInputFileName[sId],
"inputFileName_" + std::to_string(m_surfaceIds[sId]));
2430 file.setAttribute(m_noSurfaceElementsGlobal[sId],
"noSurfaceElements_" + std::to_string(m_surfaceIds[sId]));
2431 file.setAttribute(m_surfaceWeightingFactor[sId],
"surfaceWeight_" + std::to_string(m_surfaceIds[sId]));
2433 file.setAttribute(m_observerFileName,
"observerFileName");
2436 if(domainId() == 0) {
2437 file.setOffset(globalNoObservers(), 0, 2);
2439 file.setOffset(0, 0, 2);
2441 file.writeArray(m_globalObserverCoordinates.data(),
"coordinates");
2444 if(domainId() == 0) {
2445 file.setOffset(noSamples(), 0);
2447 file.setOffset(0, 0);
2449 file.writeArray(&m_times[0],
"time");
2450 file.writeArray(&m_frequencies[0],
"frequency");
2452 file.setOffset(noObservers(), observerOffset(), 2);
2453 ScratchSpace<MFloat> pressure_time(std::max(noObservers(), 1), noSamples(), AT_,
"pressure_time");
2454 for(
MInt obs = 0; obs < noObservers(); obs++) {
2455 for(
MInt t = 0; t < noSamples(); t++) {
2456 pressure_time(obs, t) = m_observerData.variables(obs, 0, t);
2459 file.writeArray(&pressure_time[0],
"pressure_time");
2461 file.setOffset(noObservers(), observerOffset(), 2);
2462 ScratchSpace<MFloat> pressure_frequency(std::max(noObservers(), 1), 2 * noSamples(), AT_,
"pressure_frequency");
2463 for(
MInt obs = 0; obs < noObservers(); obs++) {
2464 for(
MInt f = 0; f < noSamples(); f++) {
2465 pressure_frequency(obs, 2 * f) = m_observerData.complexVariables(obs, 0, f, 0);
2466 pressure_frequency(obs, 2 * f + 1) = m_observerData.complexVariables(obs, 0, f, 1);
2469 file.writeArray(&pressure_frequency[0],
"pressure_frequency");
2471 RECORD_TIMER_STOP(m_timers[Timers::SaveObserverSignals]);
2479 RECORD_TIMER_START(m_timers[Timers::LoadObserverSignals]);
2482 const MInt obsOffset = observerOffset();
2483 const MInt obsCount = noObservers();
2485 std::stringstream ss;
2486 ss <<
"- Load observer signals from file: " << m_acaPostprocessingFile << std::endl;
2487 printMessage(ss.str());
2488 ParallelIo file(m_acaPostprocessingFile, PIO_READ, mpiComm());
2491 if(domainId() == 0) {
2492 file.setOffset(noSamples(), 0);
2494 file.setOffset(0, 0);
2496 file.readArray(&m_times[0],
"time");
2497 file.readArray(&m_frequencies[0],
"frequency");
2503 file.setOffset(obsCount, obsOffset, 2);
2506 file.readArray(&pressure_time[0],
"pressure_time");
2507 for(
MInt obs = 0; obs < obsCount; obs++) {
2508 for(
MInt t = 0; t < noSamples(); t++) {
2509 m_observerData.variables(obs, 0, t) = pressure_time(obs, t);
2514 file.setOffset(obsCount, obsOffset, 2);
2515 ScratchSpace<MFloat> pressure_frequency(std::max(obsCount, 1), 2 * noSamples(), AT_,
"pressure_frequency");
2516 file.readArray(&pressure_frequency[0],
"pressure_frequency");
2518 for(
MInt obs = 0; obs < obsCount; obs++) {
2519 for(
MInt f = 0; f < noSamples(); f++) {
2520 m_observerData.complexVariables(obs, 0, f, 0) = pressure_frequency(obs, 2 * f);
2521 m_observerData.complexVariables(obs, 0, f, 1) = pressure_frequency(obs, 2 * f + 1);
2525 RECORD_TIMER_STOP(m_timers[Timers::LoadObserverSignals]);
2533 RECORD_TIMER_START(m_timers[Timers::Postprocessing]);
2534 printMessage(
"- Post process observer signals");
2537 for(
MInt op = 0; op < m_noPostprocessingOps; op++) {
2538 const MInt operation = m_postprocessingOps[op];
2540 case PP::RMS_PRESSURE: {
2556 case PP::calcObsPress: {
2557 TERMM_IF_NOT_COND(m_generateSurfaceData,
"Only useful for analytical testcases with generated surface data.");
2558 calcObsPressureAnalytic();
2563 <<
"skippinginvalid postprocessing operation: " << operation << std::endl;
2569 for(
auto&& post : m_post) {
2570 post->init(noObservers(), globalNoObservers(), observerOffset(), noSamples(), m_globalObserverCoordinates.data(),
2571 m_frequencies.data(), outputDir() + m_outputFilePrefix);
2572 for(
MInt obs = 0; obs < noObservers(); obs++) {
2573 post->calc(obs, &m_observerData.variables(obs, 0), &m_observerData.complexVariables(obs, 0));
2578 RECORD_TIMER_STOP(m_timers[Timers::Postprocessing]);
2586 const MInt noVars = (m_acousticMethod ==
FWH_METHOD) ? nDim + 2 : nDim + 1;
2590 for(
MInt obsId = 0; obsId < noObservers(); obsId++) {
2592 const MFloat* obsCoord = &m_observerData.observerCoordinates(obsId);
2595 sourceVars.
p = &vars(FWH::P, obsId, 0);
2596 sourceVars.
u = &vars(FWH::U, obsId, 0);
2597 sourceVars.
v = &vars(FWH::V, obsId, 0);
2598 sourceVars.
w = &vars(FWH::W, obsId, 0);
2599 sourceVars.
rho = &vars(FWH::RHO, obsId, 0);
2601 sourceVars.
p = &vars(FWH_APE::PP, obsId, 0);
2602 sourceVars.
u = &vars(FWH_APE::UP, obsId, 0);
2603 sourceVars.
v = &vars(FWH_APE::VP, obsId, 0);
2604 sourceVars.
w = &vars(FWH_APE::WP, obsId, 0);
2607 if constexpr(nDim == 2) {
2608 switch(m_sourceType) {
2610 genMonopoleAnalytic2D(obsCoord, m_sourceParameters, sourceVars);
2614 genDipoleAnalytic2D(obsCoord, m_sourceParameters, sourceVars);
2618 genQuadrupoleAnalytic2D(obsCoord, m_sourceParameters, sourceVars);
2622 genVortexConvectionAnalytic2D(obsCoord, m_sourceParameters, sourceVars);
2626 TERMM(1,
"source type not implemented");
2631 switch(m_sourceType) {
2633 genMonopoleAnalytic3D(obsCoord, m_sourceParameters, sourceVars);
2637 genDipoleAnalytic3D(obsCoord, m_sourceParameters, sourceVars);
2641 genQuadrupoleAnalytic3D(obsCoord, m_sourceParameters, sourceVars);
2645 TERMM(1,
"source type not implemented");
2654 const MString fileName = outputDir() + m_outputFilePrefix +
"obsPressAnalytic" + ParallelIo::fileExt();
2656 ParallelIo file(fileName, PIO_REPLACE, mpiComm());
2658 ParallelIo::size_type dimSizes[] = {globalNoObservers(), noSamples()};
2659 file.defineArray(PIO_FLOAT,
"obsPressAnalytic", 2, &dimSizes[0]);
2661 file.setOffset(noObservers(), observerOffset(), 2);
2663 file.writeArray(&vars(FWH::P, 0, 0),
"obsPressAnalytic");
2665 file.writeArray(&vars(FWH_APE::PP, 0, 0),
"obsPressAnalytic");
2674 printMessage(
"- Init parallelization");
2675 RECORD_TIMER_START(m_timers[Timers::InitParallelization]);
2677 m_noSurfaceElements.resize(noSurfaces());
2678 m_noSurfaceElementsGlobal.resize(noSurfaces());
2679 m_surfaceElementOffsets.resize(noSurfaces());
2680 m_surfaceInputFileName.resize(noSurfaces(),
"");
2682 std::vector<MInt> totalNoSegments(noSurfaces());
2685 if(m_generateSurfaceData) {
2686 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2687 std::vector<MInt> dummyVec{};
2690 totalNoSegments[sId] = readNoSegmentsSurfaceGeneration(sId, dummyVec);
2693 m_totalNoSamples = -1;
2696 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2697 MInt noSamples = -1;
2698 readNoSegmentsAndSamples(sId, totalNoSegments[sId], noSamples, m_surfaceInputFileName[sId]);
2701 TERMM_IF_NOT_COND(m_totalNoSamples < 0 || m_totalNoSamples == noSamples,
2702 "number of samples mismatch for different surfaces");
2703 m_totalNoSamples = noSamples;
2706 m_noSamples = (m_noSamples == -1) ? m_totalNoSamples : m_noSamples;
2709 const MInt usedSampleRange = m_noSamples * m_sampleStride + m_sampleOffset;
2710 TERMM_IF_COND(m_noSamples < 1 || m_sampleStride < 1 || m_sampleOffset < 0 || usedSampleRange > m_totalNoSamples,
2711 "Error: number of samples and stride need to be > 0, offset >= 0 and samples*stride+offset cannot "
2712 "exceed the total number of samples (noSamples="
2713 + std::to_string(m_noSamples) +
", stride=" + std::to_string(m_sampleStride) +
", offset="
2714 + std::to_string(m_sampleOffset) +
", samples*stride+offset=" + std::to_string(usedSampleRange)
2715 +
", totalNoSamples=" + std::to_string(m_totalNoSamples) +
").");
2718 if(m_noSamples % 2 > 0) {
2719 m_noSamples = m_noSamples - 1;
2720 if(domainId() == 0) {
2721 std::cout <<
"Note: Number of Samples was uneven which is not feasible. The new number of samples is: "
2722 << m_noSamples << std::endl;
2728 std::copy(totalNoSegments.begin(), totalNoSegments.end(), &m_noSurfaceElementsGlobal[0]);
2731 const MInt globalNoSegments = std::accumulate(totalNoSegments.begin(), totalNoSegments.end(), 0);
2733 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2734 m_log <<
"Surface #" << m_surfaceIds[sId] <<
" has " << totalNoSegments[sId]
2735 <<
" segments; surface input file name: " << m_surfaceInputFileName[sId]
2736 <<
"; weighting factor: " << m_surfaceWeightingFactor[sId] << std::endl;
2738 m_log <<
"Global number of segments: " << globalNoSegments << std::endl;
2739 m_log <<
"Number of samples: " << m_noSamples <<
" (totalNoSamples = " << m_totalNoSamples
2740 <<
", stride = " << m_sampleStride <<
", offset = " << m_sampleOffset <<
")" << std::endl;
2742 MInt localNoSegments = -1;
2748 if(m_allowMultipleSurfacesPerRank) {
2750 const MInt avgNoSegments = floor(
static_cast<MFloat>(globalNoSegments) /
static_cast<MFloat>(noDomains()));
2751 localNoSegments = avgNoSegments;
2753 if(domainId() < globalNoSegments - noDomains() * avgNoSegments) {
2754 localNoSegments += 1;
2757 std::vector<MInt> noDomainsPerSurface(noSurfaces());
2759 const MFloat avgNoSegments =
static_cast<MFloat>(globalNoSegments) /
static_cast<MFloat>(noDomains());
2761 for(
MInt i = 0; i < noSurfaces(); i++) {
2762 noDomainsPerSurface[i] = std::max(1,
static_cast<MInt>(std::floor(totalNoSegments[i] / avgNoSegments)));
2765 while(noDomains() - std::accumulate(noDomainsPerSurface.begin(), noDomainsPerSurface.end(), 0) > 0) {
2768 for(
MInt i = 0; i < noSurfaces(); i++) {
2769 const MFloat weight = totalNoSegments[i] / noDomainsPerSurface[i];
2770 if(weight > maxWeight) {
2775 noDomainsPerSurface[maxIndex]++;
2777 const MInt noDomainsSum = std::accumulate(noDomainsPerSurface.begin(), noDomainsPerSurface.end(), 0);
2778 TERMM_IF_NOT_COND(noDomainsSum == noDomains(),
2779 "number of domains mismatch " + std::to_string(noDomainsSum) +
" " + std::to_string(noDomains()));
2782 MInt localSurfaceId = -1;
2783 MInt localSurfaceDomainId = -1;
2784 for(
MInt i = 0, sumDomains = 0; i < noSurfaces(); i++) {
2785 if(sumDomains + noDomainsPerSurface[i] > domainId()) {
2787 localSurfaceDomainId = domainId() - sumDomains;
2790 sumDomains += noDomainsPerSurface[i];
2793 const MInt avgNoSegmentsSurface = floor(totalNoSegments[localSurfaceId] / noDomainsPerSurface[localSurfaceId]);
2795 localNoSegments = avgNoSegmentsSurface;
2796 if(localSurfaceDomainId
2797 < totalNoSegments[localSurfaceId] - noDomainsPerSurface[localSurfaceId] * avgNoSegmentsSurface) {
2798 localNoSegments += 1;
2803 std::vector<MInt> noSegmentsPerDomain(noDomains());
2808 const MInt globalCount = std::accumulate(noSegmentsPerDomain.begin(), noSegmentsPerDomain.end(), 0);
2809 TERMM_IF_NOT_COND(globalCount == globalNoSegments,
"globalNoSegments mismatch");
2812 std::vector<MInt> domainSegmentOffsets(std::max(noDomains() + 1, 1));
2813 domainSegmentOffsets[0] = 0;
2814 for(
MInt i = 1; i < noDomains() + 1; i++) {
2815 domainSegmentOffsets[i] = domainSegmentOffsets[i - 1] + noSegmentsPerDomain[i - 1];
2818 MIntScratchSpace noSegments_(noDomains(), noSurfaces(), AT_,
"noSegments_");
2820 MInt surfaceOffset = 0;
2822 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2823 const MInt nextSurfaceOffset = surfaceOffset + totalNoSegments[sId];
2824 const MInt start = domainSegmentOffsets[domainId()];
2825 const MInt end = domainSegmentOffsets[domainId()] + localNoSegments;
2827 MInt noLocalSegments = 0;
2828 MInt localSegmentsOffset = 0;
2830 if(start < surfaceOffset && end >= surfaceOffset) {
2833 noLocalSegments = std::min(totalNoSegments[sId], end - surfaceOffset);
2834 localSegmentsOffset = 0;
2835 }
else if(start >= surfaceOffset && start < nextSurfaceOffset) {
2837 noLocalSegments = std::min(nextSurfaceOffset - start, end - start);
2838 localSegmentsOffset = start - surfaceOffset;
2841 m_noSurfaceElements[sId] = noLocalSegments;
2842 m_surfaceElementOffsets[sId] = localSegmentsOffset;
2845 noSegments_(domainId(), sId) = noLocalSegments;
2848 surfaceOffset = nextSurfaceOffset;
2852 mpiComm(), AT_,
"MPI_IN_PLACE",
"noSegments_[0]");
2854 m_mpiCommSurface.resize(noSurfaces());
2855 m_noDomainsSurface.resize(noSurfaces());
2856 m_domainIdSurface.resize(noSurfaces());
2859 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2861 MInt noDomains_ = 0;
2864 for(
MInt d = 0; d < noDomains(); d++) {
2865 if(noSegments_(d, sId) > 0) {
2866 domains[noDomains_] = d;
2871 m_log <<
"Create MPI communicator for surface #" << m_surfaceIds[sId] <<
" with " << noDomains_
2872 <<
" ranks:" << std::endl;
2874 for(
MInt d = 0; d < noDomains_; d++) {
2875 m_log <<
" * local rank #" << d <<
" global rank #" << domains[d] <<
" with " << noSegments_(domains[d], sId)
2876 <<
" segments" << std::endl;
2881 MPI_Group globalGroup, localGroup;
2883 MPI_Group_incl(globalGroup, noDomains_, &domains[0], &localGroup, AT_);
2887 "m_mpiCommSurface[" + std::to_string(sId) +
"]");
2892 if(noSurfaceElements(sId) > 0) {
2894 MPI_Comm_rank(m_mpiCommSurface[sId], &m_domainIdSurface[sId]);
2895 MPI_Comm_size(m_mpiCommSurface[sId], &m_noDomainsSurface[sId]);
2897 TERMM_IF_NOT_COND(m_noDomainsSurface[sId] == noDomains_,
"number of domains mismatch");
2900 m_domainIdSurface[sId] = -1;
2901 m_noDomainsSurface[sId] = -1;
2905 RECORD_TIMER_STOP(m_timers[Timers::InitParallelization]);
2915 const MString baseFileName =
"surface_data_";
2916 const MString baseFileNameSuffix =
".Netcdf";
2917 std::ostringstream os;
2918 os << m_inputDir << baseFileName << std::to_string(m_surfaceIds[surfaceId]) <<
"_" << std::setw(8)
2919 << std::setfill(
'0') << fileNo << baseFileNameSuffix;
2927 printMessage(
"- Read surface data");
2928 RECORD_TIMER_START(m_timers[Timers::ReadSurfaceData]);
2931 m_surfaceData.setNoVars(m_noVariables);
2932 m_surfaceData.setNoComplexVars(m_noComplexVariables);
2933 m_surfaceData.setNoSamples(m_noSamples);
2936 const MInt totalNoSurfaceElements_ = totalNoSurfaceElements();
2937 TERMM_IF_COND(totalNoSurfaceElements_ < 1,
"Less surface elements than computational ranks is critical.");
2938 m_surfaceData.reset(totalNoSurfaceElements_);
2941 m_times.resize(noSamples());
2942 m_frequencies.resize(noSamples());
2946 if(m_generateSurfaceData) {
2948 generateSurfaceData();
2951 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2952 if(hasSurface(sId) && m_storeSurfaceData) {
2953 storeSurfaceData(sId);
2956 }
else if(m_acaPostprocessingMode) {
2959 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
2961 if(hasSurface(sId)) {
2962 readSurfaceDataFromFile(sId);
2965 if(m_storeSurfaceData) {
2966 storeSurfaceData(sId);
2973 std::copy_n(&m_times[0], noSamples(), ×Buffer[0]);
2977 for(
MInt i = 0; i < noSamples(); i++) {
2978 TERMM_IF_COND(!
approx(timesBuffer[i], m_times[i], MFloatEps),
"Error: times for surfaces do not match.");
2984 changeDimensionsSurfaceData();
2988 RECORD_TIMER_STOP(m_timers[Timers::ReadSurfaceData]);
2999 if(m_useMergedInputFile) {
3009 const MString fileName = m_inputDir
3010 + Context::getSolverProperty<MString>(
3011 "surfaceDataFileName_" + std::to_string(m_surfaceIds[surfaceId]), m_solverId, AT_);
3012 ParallelIo file(fileName, PIO_READ, mpiComm());
3017 for(
auto& var : m_inputVars) {
3018 const MString varName = var.first;
3020 std::vector<MLong> dims = file.getArrayDims(varName);
3022 TERMM_IF_NOT_COND(noSegments < 0 || dims[0] == noSegments,
"number of segments mismatch between variables");
3023 TERMM_IF_NOT_COND(noSamples < 0 || dims[1] == noSamples,
"number of samples mismatch between variables");
3025 noSegments = dims[0];
3026 noSamples = dims[1];
3030 if(file.hasAttribute(
"inputFileName")) {
3031 file.getAttribute(&inputFileName,
"inputFileName");
3036 const MString baseFileName =
"surface_data_";
3037 const MString baseFileNameSuffix =
".Netcdf";
3038 const MString varName =
"pointStates";
3039 for(
MInt fileNo = m_inputFileIndexStart; fileNo < m_inputFileIndexEnd + 1; fileNo++) {
3040 const MString fileName = getSurfaceDataFileName(surfaceId, fileNo);
3041 ParallelIo file(fileName, PIO_READ, mpiComm());
3043 std::vector<MLong> dims = file.getArrayDims(varName);
3045 TERMM_IF_COND(noSegments != dims[0] && noSegments != 0,
"number of segments mismatch between files");
3046 TERMM_IF_COND(dims[2] < (
MInt)m_inputVars.size(),
"number of input vars > number of variables in file");
3048 noSegments = dims[0];
3049 noSamples += dims[1];
3054 if(file.hasAttribute(
"inputFileName", varName)) {
3055 file.getAttribute(&inputFileName,
"inputFileName", varName);
3068 const MInt count = m_noSurfaceElements[surfaceId];
3069 const MInt offset = m_surfaceElementOffsets[surfaceId];
3070 const MInt surfaceOffset = localSurfaceOffset(surfaceId);
3072 const MString measureName = (nDim == 3) ?
"area" :
"length";
3075 auto createSurfaceElements = [&](
const MInt l_surfaceId,
3079 MFloat totalSurfaceMeasure = 0.0;
3080 MFloat totalSurfaceMeasureWeighted = 0.0;
3082 const MFloat weight = m_surfaceWeightingFactor[l_surfaceId];
3083 for(
MInt i = 0; i < count; i++) {
3084 const MInt id = surfaceOffset + i;
3086 m_surfaceData.append();
3088 TERMM_IF_NOT_COND(m_surfaceData.size() ==
id + 1,
"m_surfaceData size mismatch.");
3090 MFloat* surfCoordinates = &m_surfaceData.surfaceCoordinates(
id);
3091 MFloat* surfNormal = &m_surfaceData.surfaceNormal(
id);
3094 m_surfaceData.surfaceArea(
id) = weight * l_measure[i];
3095 totalSurfaceMeasure += l_measure[i];
3096 totalSurfaceMeasureWeighted += (weight * l_measure[i]);
3099 for(
MInt j = 0; j < nDim; j++) {
3100 surfCoordinates[j] = l_coordinates[i * nDim + j];
3101 surfNormal[j] = l_normalVector[i * nDim + j];
3107 m_mpiCommSurface[surfaceId], AT_,
"MPI_IN_PLACE",
"totalSurfaceMeasure");
3109 m_mpiCommSurface[surfaceId], AT_,
"MPI_IN_PLACE",
"totalSurfaceMeasureWeighted");
3110 if(m_domainIdSurface[surfaceId] == 0) {
3111 std::cout <<
"Total " << measureName <<
" of surface #" << surfaceId <<
": " << totalSurfaceMeasure
3112 <<
" (weighted: " << totalSurfaceMeasureWeighted <<
")" << std::endl;
3116 if(m_useMergedInputFile) {
3117 const MString fileName = m_inputDir
3118 + Context::getSolverProperty<MString>(
3119 "surfaceDataFileName_" + std::to_string(m_surfaceIds[surfaceId]), m_solverId, AT_);
3120 ParallelIo file(fileName, PIO_READ, m_mpiCommSurface[surfaceId]);
3127 file.setOffset(count, offset);
3128 file.readArray(&measure[0], measureName);
3131 file.setOffset(count, offset, 2);
3132 file.readArray(&coordinates[0],
"coordinates");
3133 file.readArray(&normalVector[0],
"normalVector");
3136 createSurfaceElements(surfaceId, measure, coordinates, normalVector);
3140 if(m_domainIdSurface[surfaceId] == 0) {
3142 file.setOffset(m_totalNoSamples, 0);
3143 file.readArray(×Buffer[0],
"time");
3148 for(
MInt i = 0; i < noSamples(); i++) {
3149 TERMM_IF_COND(!
approx(timesBuffer[m_sampleOffset + i * m_sampleStride], m_times[i], MFloatEps),
3150 "Error: local times for surfaces do not match.");
3155 if(m_sampleStride == 1) {
3156 std::copy_n(×Buffer[m_sampleOffset], noSamples(), &m_times[0]);
3158 for(
MInt i = 0; i < noSamples(); i++) {
3159 m_times[i] = timesBuffer[m_sampleOffset + i * m_sampleStride];
3163 file.setOffset(0, 0);
3164 file.readArray(×Buffer[0],
"time");
3173 file.setOffset(count, offset, 2);
3178 for(
auto& var : m_inputVars) {
3179 const MString varName = var.first;
3181 if(m_domainIdSurface[surfaceId] == 0) {
3182 std::cout <<
"Surface #" << m_surfaceIds[surfaceId] <<
": Loading variable: " << varName << std::endl;
3185 file.readArray(&varData[0], varName);
3187 const MInt varIndex = var.second;
3188 for(
MInt i = 0; i < count; i++) {
3189 const MInt id = surfaceOffset + i;
3190 for(
MInt t = 0; t < noSamples(); t++) {
3191 m_surfaceData.variables(
id, varIndex, t) = varData(i, m_sampleOffset + t * m_sampleStride);
3196 TERMM_IF_COND(m_sampleOffset > 0,
"Sample offset for multiple input files not supported yet");
3197 TERMM_IF_COND(m_sampleStride > 1,
"Sample stride for multiple input files not supported yet");
3199 const MInt noDom = m_noDomainsSurface[surfaceId];
3200 std::vector<MInt> sendCount(noDom);
3201 std::vector<MInt> sendCountNdim(noDom);
3202 std::vector<MInt> displs(noDom);
3203 std::vector<MInt> displsNdim(noDom);
3205 std::fill(sendCount.begin(), sendCount.end(), 0);
3206 std::fill(displs.begin(), displs.end(), 0);
3207 sendCount[m_domainIdSurface[surfaceId]] = count;
3208 displs[m_domainIdSurface[surfaceId]] = offset;
3209 MPI_Allreduce(MPI_IN_PLACE, sendCount.data(), sendCount.size(), MPI_INT, MPI_SUM, m_mpiCommSurface[surfaceId],
3210 AT_,
"MPI_IN_PLACE",
"sendCount");
3211 MPI_Allreduce(MPI_IN_PLACE, displs.data(), displs.size(), MPI_INT, MPI_SUM, m_mpiCommSurface[surfaceId], AT_,
3212 "MPI_IN_PLACE",
"displs");
3213 for(
MInt i = 0; i < (
MInt)sendCount.size(); i++) {
3214 sendCountNdim[i] = sendCount[i] * nDim;
3215 displsNdim[i] = displs[i] * nDim;
3218 const MInt noTotalCount = std::accumulate(sendCount.begin(), sendCount.end(), 0);
3221 std::vector<MInt> sortIndex;
3223 auto readSortIndex = [&](
const MInt fileNo, std::vector<MInt>& sortIndex_) {
3225 const MString fileName = getSurfaceDataFileName(surfaceId, fileNo);
3226 ParallelIo file(fileName, PIO_READ, m_mpiCommSurface[surfaceId]);
3228 const MString varName =
"sortIndex";
3229 if(m_domainIdSurface[surfaceId] == 0) {
3230 sortIndex_.resize(noTotalCount);
3231 file.setOffset(noTotalCount, 0);
3232 file.readArray(sortIndex_.data(), varName);
3234 sortIndex_.resize(0);
3235 file.setOffset(0, 0);
3236 file.readArray(sortIndex_.data(), varName);
3239 readSortIndex(m_inputFileIndexStart, sortIndex);
3241 std::vector<MInt> tmpSortIndex(sortIndex.size());
3242 for(
MInt fileNo = 1 + m_inputFileIndexStart; fileNo < m_inputFileIndexEnd + 1; fileNo++) {
3243 readSortIndex(fileNo, tmpSortIndex);
3244 if(!std::equal(sortIndex.begin(), sortIndex.end(), tmpSortIndex.begin())) {
3245 std::stringstream ss;
3246 ss <<
"SortIndex varies between ";
3247 ss << getSurfaceDataFileName(surfaceId, m_inputFileIndexStart);
3249 ss << getSurfaceDataFileName(surfaceId, fileNo);
3262 const MString varName =
"geomMeasure";
3263 const MString baseFileNameSuffix =
".Netcdf";
3264 const MString baseGeometryFileName =
"surface_geometryInfo_";
3265 std::ostringstream fileName;
3266 fileName << m_inputDir << baseGeometryFileName << std::to_string(m_surfaceIds[surfaceId]) << baseFileNameSuffix;
3267 ParallelIo file(fileName.str(), PIO_READ, m_mpiCommSurface[surfaceId]);
3268 if(m_domainIdSurface[surfaceId] == 0) {
3272 file.setOffset(noTotalCount, 0);
3273 file.readArray(readBuffer.
data(), varName);
3274 for(
MInt i = 0; i < noTotalCount; i++) {
3275 sendBuffer(i) = readBuffer(sortIndex[i]);
3277 MPI_Scatterv(sendBuffer.
data(), sendCount.data(), displs.data(), MPI_DOUBLE, measure.
data(), count, MPI_DOUBLE,
3278 0, m_mpiCommSurface[surfaceId], AT_,
"send",
"recv");
3281 file.setOffset(noTotalCount * nDim, 0);
3282 file.readArray(readBuffer.
data(),
"centroid");
3283 for(
MInt i = 0; i < noTotalCount; i++) {
3284 for(
MInt j = 0; j < nDim; j++) {
3285 sendBuffer(i * nDim + j) = readBuffer(sortIndex[i] * nDim + j);
3288 MPI_Scatterv(sendBuffer.
data(), sendCountNdim.data(), displsNdim.data(), MPI_DOUBLE, coordinates.
data(),
3289 count * nDim, MPI_DOUBLE, 0, m_mpiCommSurface[surfaceId], AT_,
"send",
"recv");
3290 file.readArray(readBuffer.
data(),
"normalVector");
3291 for(
MInt i = 0; i < noTotalCount; i++) {
3292 for(
MInt j = 0; j < nDim; j++) {
3293 sendBuffer(i * nDim + j) = readBuffer(sortIndex[i] * nDim + j);
3296 MPI_Scatterv(sendBuffer.
data(), sendCountNdim.data(), displsNdim.data(), MPI_DOUBLE, normalVector.
data(),
3297 count * nDim, MPI_DOUBLE, 0, m_mpiCommSurface[surfaceId], AT_,
"send",
"recv");
3301 MInt* dummySendBuffer =
nullptr;
3302 file.setOffset(0, 0);
3303 file.readArray(dummySendBuffer, varName);
3304 MPI_Scatterv(dummySendBuffer, sendCount.data(), displs.data(), MPI_DOUBLE, measure.
data(), count, MPI_DOUBLE, 0,
3305 m_mpiCommSurface[surfaceId], AT_,
"send",
"recv");
3306 file.readArray(dummySendBuffer,
"centroid");
3307 MPI_Scatterv(dummySendBuffer, sendCountNdim.data(), displsNdim.data(), MPI_DOUBLE, coordinates.
data(),
3308 count * nDim, MPI_DOUBLE, 0, m_mpiCommSurface[surfaceId], AT_,
"send",
"recv");
3309 file.readArray(dummySendBuffer,
"normalVector");
3310 MPI_Scatterv(dummySendBuffer, sendCountNdim.data(), displsNdim.data(), MPI_DOUBLE, normalVector.
data(),
3311 count * nDim, MPI_DOUBLE, 0, m_mpiCommSurface[surfaceId], AT_,
"send",
"recv");
3315 createSurfaceElements(surfaceId, measure, coordinates, normalVector);
3320 const MString varName =
"time";
3323 if(m_domainIdSurface[surfaceId] == 0) {
3324 MInt offsetInBuffer = 0;
3325 for(
MInt fileNo = m_inputFileIndexStart; fileNo < m_inputFileIndexEnd + 1; fileNo++) {
3327 const MString fileName = getSurfaceDataFileName(surfaceId, fileNo);
3328 ParallelIo file(fileName, PIO_READ, m_mpiCommSurface[surfaceId]);
3330 std::vector<MLong> dims = file.getArrayDims(varName);
3331 const MInt samplesInFile = dims[0];
3333 file.setOffset(samplesInFile, 0);
3334 file.readArray(×Buffer[offsetInBuffer], varName);
3336 offsetInBuffer += samplesInFile;
3341 for(
MInt i = 0; i < noSamples(); i++) {
3342 TERMM_IF_COND(!
approx(timesBuffer[m_sampleOffset + i * m_sampleStride], m_times[i], MFloatEps),
3343 "Error: local times for surfaces do not match.");
3348 if(m_sampleStride == 1) {
3349 std::copy_n(×Buffer[m_sampleOffset], noSamples(), &m_times[0]);
3351 for(
MInt i = 0; i < noSamples(); i++) {
3352 m_times[i] = timesBuffer[m_sampleOffset + i * m_sampleStride];
3356 for(
MInt fileNo = m_inputFileIndexStart; fileNo < m_inputFileIndexEnd + 1; fileNo++) {
3358 const MString fileName = getSurfaceDataFileName(surfaceId, fileNo);
3360 ParallelIo file(fileName, PIO_READ, m_mpiCommSurface[surfaceId]);
3361 file.setOffset(0, 0);
3362 file.readArray(×Buffer[0], varName);
3372 const MInt noVars = m_inputVars.size();
3373 MInt offsetInBuffer = 0;
3374 for(
MInt fileNo = m_inputFileIndexStart; fileNo < m_inputFileIndexEnd + 1; fileNo++) {
3376 const MString fileName = getSurfaceDataFileName(surfaceId, fileNo);
3377 ParallelIo file(fileName, PIO_READ, m_mpiCommSurface[surfaceId]);
3382 const MString varName =
"pointStates";
3383 file.setOffset(count, offset, 3);
3385 std::vector<MLong> dims = file.getArrayDims(varName);
3386 const MInt samplesInFile = dims[1];
3389 file.readArray(varData.
data(), varName);
3392 std::vector<MInt> varOrder;
3394 std::map<MInt, MString> fileVars{};
3395 for(
MInt varIndex = 0; varIndex < noVars; varIndex++) {
3397 file.getAttribute(&var,
"var_" + std::to_string(varIndex), varName);
3398 fileVars[varIndex] = var;
3400 for(
MInt varIndex = 0; varIndex < noVars; varIndex++) {
3401 varOrder.push_back(m_inputVars[fileVars[varIndex]]);
3406 for(
MInt i = 0; i < count; i++) {
3407 const MInt id = surfaceOffset + i;
3408 for(
MInt t = 0; t < samplesInFile && offsetInBuffer + t < noSamples(); t++) {
3409 for(
MInt varIndex = 0; varIndex < noVars; varIndex++) {
3410 const MInt var = varOrder[varIndex];
3413 m_surfaceData.variables(
id, var, offsetInBuffer + t) = varData(
id, t, varIndex);
3417 offsetInBuffer += samplesInFile;
3431 const MString fileName = outputDir() + m_outputFilePrefix +
"out_surfaceData_"
3432 + std::to_string(m_surfaceIds[surfaceId]) + ParallelIo::fileExt();
3433 std::stringstream ss;
3435 <<
"Testing: Storing surface data in " << fileName;
3436 printMessage(ss.str());
3438 ParallelIo file(fileName, PIO_REPLACE, m_mpiCommSurface[surfaceId]);
3440 const MInt totalCount = m_noSurfaceElementsGlobal[surfaceId];
3441 const MInt count = m_noSurfaceElements[surfaceId];
3442 const MInt offset = m_surfaceElementOffsets[surfaceId];
3444 const MString measureName = (nDim == 3) ?
"area" :
"length";
3445 file.defineArray(PIO_FLOAT, measureName, totalCount);
3447 file.defineArray(PIO_FLOAT,
"time", noSamples());
3449 ParallelIo::size_type dimSizesXD[] = {totalCount, nDim};
3451 file.defineArray(PIO_FLOAT,
"coordinates", 2, &dimSizesXD[0]);
3452 file.defineArray(PIO_FLOAT,
"normalVector", 2, &dimSizesXD[0]);
3454 ParallelIo::size_type dimSizesVar[] = {totalCount, noSamples()};
3456 for(
auto& var : m_inputVars) {
3457 const MString varName = var.first;
3458 file.defineArray(PIO_FLOAT, varName, 2, &dimSizesVar[0]);
3461 const MInt surfaceOffset = localSurfaceOffset(surfaceId);
3464 file.setOffset(count, offset);
3465 file.writeArray(&m_surfaceData.surfaceArea(surfaceOffset), measureName);
3468 file.setOffset(((m_domainIdSurface[surfaceId] == 0) ? noSamples() : 0), 0);
3469 file.writeArray(&m_times[0],
"time");
3472 file.setOffset(count, offset, 2);
3473 file.writeArray(&m_surfaceData.surfaceCoordinates(surfaceOffset),
"coordinates");
3474 file.writeArray(&m_surfaceData.surfaceNormal(surfaceOffset),
"normalVector");
3476 file.setOffset(count, offset, 2);
3478 for(
auto& var : m_inputVars) {
3479 const MInt varIndex = var.second;
3480 for(
MInt i = 0; i < count; i++) {
3481 for(
MInt t = 0; t < noSamples(); t++) {
3482 varBuffer(i, t) = m_surfaceData.variables(surfaceOffset + i, varIndex, t);
3486 file.writeArray(&varBuffer[0], var.first);
3497 for(
MInt sId = 0; sId < noSurfaces(); sId++) {
3512 const MInt surfaceType =
3513 Context::getSolverProperty<MInt>(
"surfaceType_" + std::to_string(m_surfaceIds[sId]), solverId(), AT_);
3515 m_surfaceInputFileName[sId] = (surfaceType == 0) ?
"generated plane" :
"generated circle";
3518 if(!hasSurface(sId)) {
3522 switch(surfaceType) {
3524 generateSurfacePlane(sId);
3527 generateSurfaceCircle(sId);
3530 TERMM(1,
"Unknown surface type: " + std::to_string(surfaceType) +
"; possible values: 0 - plane.");
3542 const MInt lengthCheck) {
3552 const MString noSegmentsName =
"noSegments_" + std::to_string(m_surfaceIds[sId]);
3555 if(lengthCheck > 0) {
3559 MInt totalNoSegments = 1;
3561 noSegments.resize(propLength);
3564 for(
MInt i = 0; i < propLength; i++) {
3565 noSegments[i] = Context::getSolverProperty<MInt>(noSegmentsName, m_solverId, AT_, i);
3566 TERMM_IF_NOT_COND(noSegments[i] >= 0,
"Number of segments needs to be >= 0");
3569 if(noSegments[i] > 0) {
3570 totalNoSegments *= noSegments[i];
3574 TERMM_IF_NOT_COND(totalNoSegments > 0,
"Total number of segments needs to be > 0");
3575 return totalNoSegments;
3585 const MFloat weight = m_surfaceWeightingFactor[sId];
3587 if constexpr(nDim == 3) {
3589 std::vector<MInt> noSegmentsPerPlane{};
3591 MInt totalNoSeg = readNoSegmentsSurfaceGeneration(sId, noSegmentsPerPlane, nDim);
3602 const MInt surfaceNormalDir =
3603 Context::getSolverProperty<MInt>(
"surfaceNormalDir_" + std::to_string(m_surfaceIds[sId]), m_solverId, AT_);
3604 TERMM_IF_NOT_COND(surfaceNormalDir >= 0 && surfaceNormalDir < 2 * nDim,
3605 "Invalid surface outer normal direction: " + std::to_string(surfaceNormalDir));
3608 std::vector<MFloat> surfaceNormal(nDim, 0.0);
3609 for(
MInt i = 0; i < nDim; i++) {
3610 if(surfaceNormalDir == 2 * i) {
3611 surfaceNormal[i] = -1.0;
3612 }
else if(surfaceNormalDir == 2 * i + 1) {
3613 surfaceNormal[i] = 1.0;
3615 surfaceNormal[i] = 0.0;
3621 std::vector<MFloat> planeCoordinates(2 * nDim, 0.0);
3623 const MInt noCoords = 2 * nDim;
3634 const MString coordName =
"surfaceCoordinates_" + std::to_string(m_surfaceIds[sId]);
3638 for(
MInt i = 0; i < noCoords; i++) {
3639 planeCoordinates[i] = Context::getSolverProperty<MFloat>(coordName, m_solverId, AT_, i);
3643 const MFloat length_x = std::abs(planeCoordinates[nDim] - planeCoordinates[0]);
3644 const MFloat length_y = std::abs(planeCoordinates[nDim + 1] - planeCoordinates[1]);
3645 const MFloat length_z = std::abs(planeCoordinates[nDim + 2] - planeCoordinates[2]);
3647 MFloat start_x, start_y, start_z, noSeg_x, noSeg_y, noSeg_z, stepSize_x, stepSize_y, stepSize_z;
3649 if(
approx(length_x, 0.0, MFloatEps)) {
3650 start_x = planeCoordinates[0];
3655 Context::getSolverProperty<MFloat>(
"noSegments_" + std::to_string(m_surfaceIds[sId]), m_solverId, AT_, 0);
3656 stepSize_x = length_x / noSeg_x;
3657 start_x = planeCoordinates[0] + stepSize_x / 2.0;
3660 if(
approx(length_y, 0.0, MFloatEps)) {
3661 start_y = planeCoordinates[1];
3666 Context::getSolverProperty<MFloat>(
"noSegments_" + std::to_string(m_surfaceIds[sId]), m_solverId, AT_, 1);
3667 stepSize_y = length_y / noSeg_y;
3668 start_y = planeCoordinates[1] + stepSize_y / 2.0;
3671 if(
approx(length_z, 0.0, MFloatEps)) {
3672 start_z = planeCoordinates[2];
3677 Context::getSolverProperty<MFloat>(
"noSegments_" + std::to_string(m_surfaceIds[sId]), m_solverId, AT_, 2);
3678 stepSize_z = length_z / noSeg_z;
3679 start_z = planeCoordinates[2] + stepSize_z / 2.0;
3684 (
approx(length_x, 0.0, MFloatEps) ||
approx(length_y, 0.0, MFloatEps) ||
approx(length_z, 0.0, MFloatEps)),
3685 "Plane not axis aligned.");
3687 std::vector<MFloat> coordinates_buff(nDim * totalNoSeg, 0.0);
3688 MFloat delta_segment = 0.0;
3690 if(
approx(stepSize_x, 0.0, MFloatEps)) {
3691 delta_segment = stepSize_y * stepSize_z;
3693 for(
MInt j = 0; j < noSeg_z; j++) {
3694 for(
MInt i = 0; i < noSeg_y; i++) {
3695 coordinates_buff[j * 3 * noSeg_y + i * 3] = start_x;
3696 coordinates_buff[j * 3 * noSeg_y + i * 3 + 1] = start_y + i * stepSize_y;
3697 coordinates_buff[j * 3 * noSeg_y + i * 3 + 2] = start_z + j * stepSize_z;
3700 }
else if(
approx(stepSize_y, 0.0, MFloatEps)) {
3701 delta_segment = stepSize_x * stepSize_z;
3703 for(
MInt j = 0; j < noSeg_z; j++) {
3704 for(
MInt i = 0; i < noSeg_x; i++) {
3705 coordinates_buff[j * 3 * noSeg_x + i * 3] = start_x + i * stepSize_x;
3706 coordinates_buff[j * 3 * noSeg_x + i * 3 + 1] = start_y;
3707 coordinates_buff[j * 3 * noSeg_x + i * 3 + 2] = start_z + j * stepSize_z;
3711 }
else if(
approx(stepSize_z, 0.0, MFloatEps)) {
3712 delta_segment = stepSize_x * stepSize_y;
3714 for(
MInt j = 0; j < noSeg_x; j++) {
3715 for(
MInt i = 0; i < noSeg_y; i++) {
3716 coordinates_buff[j * 3 * noSeg_y + i * 3] = start_x + j * stepSize_x;
3717 coordinates_buff[j * 3 * noSeg_y + i * 3 + 1] = start_y + i * stepSize_y;
3718 coordinates_buff[j * 3 * noSeg_y + i * 3 + 2] = start_z;
3724 const MInt surfaceOffset = localSurfaceOffset(sId);
3727 for(
MInt i = 0; i < m_noSurfaceElements[sId]; i++) {
3728 const MInt id = surfaceOffset + i;
3730 m_surfaceData.append();
3732 TERMM_IF_NOT_COND(m_surfaceData.size() ==
id + 1,
"m_surfaceData size mismatch.");
3736 const MInt segmentId = i + m_surfaceElementOffsets[sId];
3738 TERMM_IF_NOT_COND(segmentId >= 0 && segmentId < totalNoSeg,
"invalid segment id");
3741 const MFloat xcenter = coordinates_buff[3 * segmentId];
3742 const MFloat ycenter = coordinates_buff[3 * segmentId + 1];
3743 const MFloat zcenter = coordinates_buff[3 * segmentId + 2];
3746 MFloat* surfCoordinates = &m_surfaceData.surfaceCoordinates(
id);
3747 surfCoordinates[0] = xcenter;
3748 surfCoordinates[1] = ycenter;
3749 surfCoordinates[2] = zcenter;
3752 m_surfaceData.surfaceArea(
id) = weight * delta_segment;
3755 MFloat* surfNormal = &m_surfaceData.surfaceNormal(
id);
3756 surfNormal[0] = surfaceNormal[0];
3757 surfNormal[1] = surfaceNormal[1];
3758 surfNormal[2] = surfaceNormal[2];
3760 }
else if constexpr(nDim == 2) {
3762 std::vector<MInt> noSegmentsPerDir{};
3763 readNoSegmentsSurfaceGeneration(sId, noSegmentsPerDir, nDim - 1);
3766 const MInt surfaceNormalDir =
3767 Context::getSolverProperty<MInt>(
"surfaceNormalDir_" + std::to_string(m_surfaceIds[sId]), m_solverId, AT_);
3768 TERMM_IF_NOT_COND(surfaceNormalDir >= 0 && surfaceNormalDir < 2 * nDim,
3769 "Invalid surface outer normal direction: " + std::to_string(surfaceNormalDir));
3772 std::vector<MFloat> surfaceNormal(nDim, 0.0);
3773 for(
MInt i = 0; i < nDim; i++) {
3774 if(surfaceNormalDir == 2 * i) {
3775 surfaceNormal[i] = -1.0;
3776 }
else if(surfaceNormalDir == 2 * i + 1) {
3777 surfaceNormal[i] = 1.0;
3779 surfaceNormal[i] = 0.0;
3784 std::vector<MFloat> planeCoordinates(2 * nDim, 0.0);
3786 const MInt noCoords = 2 * nDim;
3787 const MString coordName =
"surfaceCoordinates_" + std::to_string(m_surfaceIds[sId]);
3791 for(
MInt i = 0; i < noCoords; i++) {
3792 planeCoordinates[i] = Context::getSolverProperty<MFloat>(coordName, m_solverId, AT_, i);
3795 const MFloat noSegmentsInv = 1.0 / (noSegmentsPerDir[0]);
3796 const MFloat Delta_x = (planeCoordinates[nDim] - planeCoordinates[0]);
3797 const MFloat Delta_y = (planeCoordinates[nDim + 1] - planeCoordinates[1]);
3800 TERMM_IF_NOT_COND(
approx(Delta_x, 0.0, MFloatEps) ||
approx(Delta_y, 0.0, MFloatEps),
"Plane not axis aligned.");
3802 const MInt surfaceOffset = localSurfaceOffset(sId);
3805 for(
MInt i = 0; i < m_noSurfaceElements[sId]; i++) {
3806 const MInt id = surfaceOffset + i;
3808 m_surfaceData.append();
3810 TERMM_IF_NOT_COND(m_surfaceData.size() ==
id + 1,
"m_surfaceData size mismatch.");
3813 const MInt segmentId = i + m_surfaceElementOffsets[sId];
3815 TERMM_IF_NOT_COND(segmentId >= 0 && segmentId < noSegmentsPerDir[0],
"invalid segment id");
3818 const MFloat p1_x = planeCoordinates[0] + segmentId * noSegmentsInv * Delta_x;
3819 const MFloat p1_y = planeCoordinates[1] + segmentId * noSegmentsInv * Delta_y;
3822 const MFloat p2_x = planeCoordinates[0] + (segmentId + 1) * noSegmentsInv * Delta_x;
3823 const MFloat p2_y = planeCoordinates[1] + (segmentId + 1) * noSegmentsInv * Delta_y;
3826 const MFloat xcenter = 0.5 * (p2_x + p1_x);
3827 const MFloat ycenter = 0.5 * (p2_y + p1_y);
3830 const MFloat dx = (p2_x - p1_x);
3831 const MFloat dy = (p2_y - p1_y);
3832 const MFloat delta_segment = sqrt(dx * dx + dy * dy);
3835 MFloat* surfCoordinates = &m_surfaceData.surfaceCoordinates(
id);
3836 surfCoordinates[0] = xcenter;
3837 surfCoordinates[1] = ycenter;
3840 m_surfaceData.surfaceArea(
id) = weight * delta_segment;
3843 MFloat* surfNormal = &m_surfaceData.surfaceNormal(
id);
3844 surfNormal[0] = surfaceNormal[0];
3845 surfNormal[1] = surfaceNormal[1];
3857 const MFloat weight = m_surfaceWeightingFactor[sId];
3859 if constexpr(nDim != 2) {
3860 TERMM(1,
"Only implemented in 2D.");
3864 TERMM(1,
"The 2D implementation is not tested and seems not to work properly.");
3867 MInt totalNoSurfaceElements = 100;
3868 totalNoSurfaceElements = Context::getSolverProperty<MInt>(
"noSegments_" + std::to_string(m_surfaceIds[sId]),
3869 m_solverId, AT_, &totalNoSurfaceElements);
3872 radius = Context::getSolverProperty<MFloat>(
"radius", m_solverId, AT_, &radius);
3874 const MFloat step_rad = 2 * PI /
static_cast<MFloat>(totalNoSurfaceElements);
3876 std::vector<MFloat> surfaceNormal(2, 0.0);
3877 const MInt surfaceOffset = localSurfaceOffset(sId);
3878 for(
MInt i = 0; i < m_noSurfaceElements[sId]; i++) {
3879 const MInt id = surfaceOffset + i;
3881 m_surfaceData.append();
3883 TERMM_IF_NOT_COND(m_surfaceData.size() ==
id + 1,
"m_surfaceData size mismatch.");
3886 const MFloat p1_x = radius * cos(i * step_rad);
3887 const MFloat p1_y = radius * sin(i * step_rad);
3890 const MFloat p2_x = radius * cos((i + 1) * step_rad);
3891 const MFloat p2_y = radius * sin((i + 1) * step_rad);
3894 const MFloat xcenter = 0.5 * (p2_x + p1_x);
3895 const MFloat ycenter = 0.5 * (p2_y + p1_y);
3898 const MFloat dx = (p2_x - p1_x);
3899 const MFloat dy = (p2_y - p1_y);
3900 const MFloat delta_segment = sqrt(dx * dx + dy * dy);
3903 MFloat* surfCoordinates = &m_surfaceData.surfaceCoordinates(
id);
3904 surfCoordinates[0] = xcenter;
3905 surfCoordinates[1] = ycenter;
3908 m_surfaceData.surfaceArea(i) = weight * delta_segment;
3911 surfaceNormal[0] = 2 * xcenter / (sqrt(4 * xcenter * xcenter + 4 * ycenter * ycenter));
3912 surfaceNormal[1] = 2 * ycenter / (sqrt(4 * xcenter * xcenter + 4 * ycenter * ycenter));
3915 MFloat* surfNormal = &m_surfaceData.surfaceNormal(
id);
3916 surfNormal[0] = surfaceNormal[0];
3917 surfNormal[1] = surfaceNormal[1];
3927 const MInt noVars = m_surfaceData.noVars();
3928 TERMM_IF_NOT_COND(noVars == m_noVariables,
"number of variables mismatch");
3931 const MBool analytic =
true;
3936 std::fill_n(&m_surfaceData.variables(0, 0, 0), totalNoSurfaceElements() * noSamples() * noVars, 0.0);
3938 TERMM_IF_NOT_COND(m_dt > 0.0,
"Invalid constant time step size: " + std::to_string(m_dt));
3941 for(
MInt t = 0; t < noSamples(); t++) {
3942 m_times[t] = m_dt * t;
3946 const MInt noSources = 1;
3948 for(
MInt sourceId = 0; sourceId < noSources; sourceId++) {
3951 for(
MInt segmentId = 0; segmentId < totalNoSurfaceElements(); segmentId++) {
3952 const MFloat* coord = &m_surfaceData.surfaceCoordinates(segmentId);
3956 sourceVars.
p = &m_surfaceData.variables(segmentId, FWH::P);
3957 sourceVars.
u = &m_surfaceData.variables(segmentId, FWH::U);
3958 sourceVars.
v = &m_surfaceData.variables(segmentId, FWH::V);
3959 sourceVars.
w = &m_surfaceData.variables(segmentId, FWH::W);
3960 sourceVars.
rho = &m_surfaceData.variables(segmentId, FWH::RHO);
3962 sourceVars.
p = &m_surfaceData.variables(segmentId, FWH_APE::PP);
3963 sourceVars.
u = &m_surfaceData.variables(segmentId, FWH_APE::UP);
3964 sourceVars.
v = &m_surfaceData.variables(segmentId, FWH_APE::VP);
3965 sourceVars.
w = &m_surfaceData.variables(segmentId, FWH_APE::WP);
3970 if constexpr(nDim == 2) {
3971 switch(m_sourceType) {
3973 genMonopoleAnalytic2D(coord, m_sourceParameters, sourceVars);
3977 genDipoleAnalytic2D(coord, m_sourceParameters, sourceVars);
3981 genQuadrupoleAnalytic2D(coord, m_sourceParameters, sourceVars);
3985 genVortexConvectionAnalytic2D(coord, m_sourceParameters, sourceVars);
3989 TERMM(1,
"source type not implemented");
3994 switch(m_sourceType) {
3996 genMonopoleAnalytic3D(coord, m_sourceParameters, sourceVars);
4000 genDipoleAnalytic3D(coord, m_sourceParameters, sourceVars);
4004 genQuadrupoleAnalytic3D(coord, m_sourceParameters, sourceVars);
4009 TERMM(1,
"source type not implemented");
4015 TERMM(1,
"Error: semi-analytic source generation not implemented.");
4038 transformCoordinatesToFlowDirection2D(&x, &
y);
4043 const MFloat p0 = rho0 * c0 * c0 / m_gamma;
4050 const MFloat k = omega / c0;
4052 const MFloat d = sqrt(x * x + beta * beta *
y *
y);
4053 const MFloat xfd = x / d;
4055 const MFloat beta2 = (beta * beta);
4056 const MFloat fbeta2 = 1 / beta2;
4057 const MFloat kfbeta2 = k / (beta * beta);
4058 const MFloat amplwf4beta = amplitude * omega / (4.0 * beta);
4059 const MFloat amplkf4beta3 = amplitude * k / (4.0 * beta * beta2);
4060 const MFloat argHankel = kfbeta2 * d;
4061 const MFloat J0 = j0(argHankel);
4062 const MFloat J1 = j1(argHankel);
4063 const MFloat Y0 = y0(argHankel);
4064 const MFloat Y1 = y1(argHankel);
4066 for(
MInt sample = 0; sample < noSamples(); sample++) {
4068 const MFloat time = sample * m_dt;
4069 const MFloat arg = omega * time + mach * k * x * fbeta2;
4071 const MFloat cosarg = cos(arg);
4072 const MFloat sinarg = sin(arg);
4074 const MFloat dPhidt = -1.0 * amplwf4beta * (cosarg * J0 + sinarg * Y0);
4076 -1.0 * mach * amplkf4beta3 * (cosarg * J0 + sinarg * Y0) + amplkf4beta3 * xfd * (sinarg * J1 - cosarg * Y1);
4077 const MFloat dPhidy = amplkf4beta3 * yfd * beta2 * (-1.0 * cosarg * Y1 + sinarg * J1);
4078 const MFloat pp = -1.0 * rho0 * (dPhidt + u0 * dPhidx);
4081 vars.
u[sample] = dPhidx;
4082 vars.
v[sample] = dPhidy;
4083 vars.
p[sample] = pp;
4085 vars.
u[sample] = dPhidx + u0;
4086 vars.
v[sample] = dPhidy;
4087 vars.
p[sample] = pp + p0;
4088 vars.
rho[sample] = rho0 + pp / (c0 * c0);
4090 transformBackToOriginalCoordinate2D(&vars.
u[sample], &vars.
v[sample]);
4101 transformCoordinatesToFlowDirection3D(&x, &
y, &z);
4106 const MFloat p0 = rho0 * c0 * c0 / m_gamma;
4116 const MFloat d = sqrt(x * x + beta * beta * (
y *
y + z * z));
4117 const MFloat xfd = x / d;
4118 const MFloat xfd2 = x / (d * d);
4120 const MFloat yfd2 =
y / (d * d);
4121 const MFloat zfd = z / d;
4122 const MFloat zfd2 = z / (d * d);
4123 const MFloat ampf4pid = amplitude / (4.0 * PI * d);
4124 const MFloat beta2 = (beta * beta);
4125 const MFloat fbeta2 = 1.0 / (beta * beta);
4127 for(
MInt sample = 0; sample < noSamples(); sample++) {
4129 const MFloat time = sample * m_dt;
4130 const MFloat arg = omega * time - k * (d - mach * x) * fbeta2;
4132 const MFloat cosarg = cos(arg);
4133 const MFloat sinarg = sin(arg);
4135 const MFloat dPhidt = -1.0 * ampf4pid * omega * sinarg;
4136 const MFloat dPhidx = -1.0 * ampf4pid * xfd2 * cosarg + ampf4pid * k * fbeta2 * sinarg * (xfd - mach);
4137 const MFloat dPhidy = -1.0 * ampf4pid * beta2 * yfd2 * cosarg + ampf4pid * k * yfd * sinarg;
4138 const MFloat dPhidz = -1.0 * ampf4pid * beta2 * zfd2 * cosarg + ampf4pid * k * zfd * sinarg;
4139 const MFloat pp = -1.0 * rho0 * (dPhidt + u0 * dPhidx);
4142 vars.
u[sample] = dPhidx;
4143 vars.
v[sample] = dPhidy;
4144 vars.
w[sample] = dPhidz;
4145 vars.
p[sample] = pp;
4147 vars.
u[sample] = dPhidx + u0;
4148 vars.
v[sample] = dPhidy;
4149 vars.
w[sample] = dPhidz;
4150 vars.
p[sample] = pp + p0;
4151 vars.
rho[sample] = rho0 + pp / (c0 * c0);
4153 transformBackToOriginalCoordinate3D(&vars.
u[sample], &vars.
v[sample], &vars.
w[sample]);
4163 transformCoordinatesToFlowDirection2D(&x, &
y);
4168 const MFloat p0 = rho0 * c0 * c0 / m_gamma;
4175 const MFloat beta2 = (beta * beta);
4176 const MFloat fbeta2 = 1 / beta2;
4180 const MFloat d = sqrt(x * x + beta2 *
y *
y);
4181 const MFloat dargdx = mach * k * fbeta2;
4182 const MFloat argHankel = k * d * fbeta2;
4183 const MFloat dHdx = k * x * fbeta2 / d;
4184 const MFloat dHdxdx = k *
y *
y / (d * d * d);
4185 const MFloat dHdxdy = -1.0 * k * x *
y / (d * d * d);
4186 const MFloat dHdy = k *
y / d;
4187 const MFloat C1 = -1.0 * mach * G * k / (4.0 * beta2 * beta);
4188 const MFloat C2 = G / (4.0 * beta);
4190 const MFloat J0 = j0(argHankel);
4191 const MFloat J1 = j1(argHankel);
4192 const MFloat dJ1 = J0 - (1 / argHankel) * J1;
4194 const MFloat Y0 = y0(argHankel);
4195 const MFloat Y1 = y1(argHankel);
4196 const MFloat dY1 = Y0 - (1 / argHankel) * Y1;
4198 for(
MInt sample = 0; sample < noSamples(); sample++) {
4199 const MFloat time = sample * m_dt;
4200 const MFloat arg = omega * time + mach * k * x * fbeta2;
4202 const MFloat cosarg = cos(arg);
4203 const MFloat sinarg = sin(arg);
4206 C1 * omega * (-1.0 * sinarg * J0 + cosarg * Y0) + C2 * dHdx * omega * (cosarg * J1 + sinarg * Y1);
4207 const MFloat dPhidx = -1.0 * C1 * (dargdx * (sinarg * J0 - cosarg * Y0) + dHdx * (cosarg * J1 + sinarg * Y1))
4209 * (dHdxdx * (sinarg * J1 - cosarg * Y1) + dHdx * dargdx * (cosarg * J1 + sinarg * Y1)
4210 + dHdx * dHdx * (sinarg * dJ1 - cosarg * dY1));
4212 const MFloat dPhidy = -1.0 * C1 * dHdy * (cosarg * J1 + sinarg * Y1)
4213 + C2 * (dHdxdy * (sinarg * J1 - cosarg * Y1) + dHdx * dHdy * (sinarg * dJ1 - cosarg * dY1));
4214 const MFloat pp = -1.0 * rho0 * (dPhidt + u0 * dPhidx);
4217 vars.
u[sample] = dPhidx;
4218 vars.
v[sample] = dPhidy;
4219 vars.
p[sample] = pp;
4221 vars.
u[sample] = dPhidx + u0;
4222 vars.
v[sample] = dPhidy;
4223 vars.
p[sample] = pp + p0;
4224 vars.
rho[sample] = rho0 + pp / (c0 * c0);
4226 transformBackToOriginalCoordinate2D(&vars.
u[sample], &vars.
v[sample]);
4237 transformCoordinatesToFlowDirection3D(&x, &
y, &z);
4242 const MFloat p0 = rho0 * c0 * c0 / m_gamma;
4250 const MFloat beta2 = beta * beta;
4251 const MFloat fbeta2 = 1 / beta2;
4252 const MFloat d = sqrt(x * x + beta * beta * (
y *
y + z * z));
4254 const MFloat fd2 = 1 / d2;
4255 const MFloat fd3 = 1 / (d2 * d);
4256 const MFloat C1 = amplitude / (4 * PI);
4257 const MFloat C2 = k * fbeta2;
4259 const MFloat P1 = fd3 - 3 * x * x * fd2 * fd3;
4260 const MFloat P2 = 1 * fd2 - 2 * x * x * fd2 * fd2;
4261 const MFloat P3 = -1.0 * x * fd3;
4263 const MFloat dargdx = -1.0 * C2 * (x / d - mach);
4264 const MFloat dargdy = -1.0 * k *
y / d;
4265 const MFloat dargdz = -1.0 * k * z / d;
4267 for(
MInt sample = 0; sample < noSamples(); sample++) {
4269 const MFloat time = sample * m_dt;
4270 const MFloat arg = omega * time - k * (d - mach * x) * fbeta2;
4272 const MFloat cosarg = cos(arg);
4273 const MFloat sinarg = sin(arg);
4276 C1 * omega * fd3 * x * sinarg + C1 * omega * fd2 * x * C2 * cosarg - C1 * omega * C2 * (mach / d) * cosarg;
4277 const MFloat dPhidx = -1.0 * C1 * P1 * cosarg + C1 * x * fd3 * dargdx * sinarg + C1 * P2 * C2 * sinarg
4278 + C1 * x * fd2 * C2 * dargdx * cosarg - C1 * C2 * P3 * mach * sinarg
4279 - C1 * C2 * (mach / d) * dargdx * cosarg;
4280 const MFloat dPhidy = C1 * x * 3 *
y * beta2 * fd3 * fd2 * cosarg + C1 * x * fd3 * dargdy * sinarg
4281 - C1 * x * C2 * 2 *
y * beta2 * fd2 * fd2 * sinarg + C1 * x * C2 * fd2 * dargdy * cosarg
4282 + C1 * C2 *
y * beta2 * mach * fd3 * sinarg - C1 * C2 * (mach / d) * dargdy * cosarg;
4283 const MFloat dPhidz = C1 * x * 3 * z * beta2 * fd3 * fd2 * cosarg + C1 * x * fd3 * dargdz * sinarg
4284 - C1 * x * C2 * 2 * z * beta2 * fd2 * fd2 * sinarg + C1 * x * C2 * fd2 * dargdz * cosarg
4285 + C1 * C2 * z * beta2 * mach * fd3 * sinarg - C1 * C2 * (mach / d) * dargdz * cosarg;
4286 const MFloat pp = -1.0 * rho0 * (dPhidt + u0 * dPhidx);
4289 vars.
u[sample] = dPhidx;
4290 vars.
v[sample] = dPhidy;
4291 vars.
w[sample] = dPhidz;
4292 vars.
p[sample] = pp;
4294 vars.
u[sample] = dPhidx + u0;
4295 vars.
v[sample] = dPhidy;
4296 vars.
w[sample] = dPhidz;
4297 vars.
p[sample] = pp + p0;
4298 vars.
rho[sample] = rho0 + pp / (c0 * c0);
4300 transformBackToOriginalCoordinate3D(&vars.
u[sample], &vars.
v[sample], &vars.
w[sample]);
4310 transformCoordinatesToFlowDirection2D(&x, &
y);
4315 const MFloat p0 = rho0 * c0 * c0 / m_gamma;
4323 const MFloat beta2 = beta * beta;
4324 const MFloat fbeta2 = 1 / beta2;
4325 const MFloat d = sqrt(x * x + beta2 *
y *
y);
4326 const MFloat dargdx = mach * k * fbeta2;
4327 const MFloat argHankel = k * d * fbeta2;
4328 const MFloat K1 = -1.0 * mach * amplitude * k / (4.0 * beta2 * beta);
4329 const MFloat K2 = amplitude / (4.0 * beta);
4331 const MFloat dHdx = k * x * fbeta2 / d;
4332 const MFloat dHdy = k *
y / d;
4334 const MFloat dHdxdx = k *
y *
y / (d * d * d);
4335 const MFloat dHdxdy = -1.0 * k * x *
y / (d * d * d);
4336 const MFloat dHdydx = dHdxdy;
4337 const MFloat dHdydy = k * x * x / (d * d * d);
4339 const MFloat dHdxdydx = k *
y * (2 * x * x - beta2 *
y *
y) / (d * d * d * d * d);
4340 const MFloat dHdxdydy = k * x * (2 * beta2 *
y *
y - x * x) / (d * d * d * d * d);
4342 const MFloat dHdxfH = x / (d * d);
4343 const MFloat dHdxfHdx = -1.0 * (x * x - beta2 *
y *
y) / (d * d * d * d);
4344 const MFloat dHdxfHdy = -1.0 * 2 * beta2 * x *
y / (d * d * d * d);
4347 const MFloat J0 = j0(argHankel);
4348 const MFloat J1 = j1(argHankel);
4349 const MFloat dJ1 = J0 - (1 / argHankel) * J1;
4351 const MFloat Y0 = y0(argHankel);
4352 const MFloat Y1 = y1(argHankel);
4353 const MFloat dY1 = Y0 - (1 / argHankel) * Y1;
4355 for(
MInt sample = 0; sample < noSamples(); sample++) {
4356 const MFloat time = sample * m_dt;
4357 const MFloat arg = omega * time + mach * k * x * fbeta2;
4359 const MFloat cosarg = cos(arg);
4360 const MFloat sinarg = sin(arg);
4362 const MFloat dPhidt = K1 * dHdy * sinarg * omega * J1 - K1 * dHdy * cosarg * omega * Y1
4363 + K2 * dHdxdy * cosarg * omega * J1 + K2 * dHdxdy * sinarg * omega * Y1
4364 + K2 * dHdx * dHdy * cosarg * omega * J0 - K2 * dHdxfH * dHdy * cosarg * omega * J1
4365 + K2 * dHdx * dHdy * sinarg * omega * Y0 - K2 * dHdxfH * dHdy * sinarg * omega * Y1;
4368 -1.0 * K1 * dHdydx * cosarg * J1 + K1 * dHdy * sinarg * dargdx * J1 - K1 * dHdy * cosarg * dJ1 * dHdx
4369 - K1 * dHdydx * sinarg * Y1 - K1 * dHdy * cosarg * dargdx * Y1 - K1 * dHdy * sinarg * dY1 * dHdx
4370 + K2 * dHdxdydx * sinarg * J1 + K2 * dHdxdy * cosarg * dargdx * J1 + K2 * dHdxdy * sinarg * dJ1 * dHdx
4371 - K2 * dHdxdydx * cosarg * Y1 + K2 * dHdxdy * sinarg * dargdx * Y1 - K2 * dHdxdy * cosarg * dY1 * dHdx
4372 + K2 * dHdxdx * dHdy * sinarg * J0 + K2 * dHdx * dHdydx * sinarg * J0 + K2 * dHdx * dHdy * cosarg * dargdx * J0
4373 - K2 * dHdx * dHdy * sinarg * J1 * dHdx - K2 * dHdxfHdx * dHdy * sinarg * J1
4374 - K2 * dHdxfH * dHdydx * sinarg * J1 - K2 * dHdxfH * dHdy * cosarg * dargdx * J1
4375 - K2 * dHdxfH * dHdy * sinarg * dJ1 * dHdx - K2 * dHdxdx * dHdy * cosarg * Y0 - K2 * dHdx * dHdydx * cosarg * Y0
4376 + K2 * dHdx * dHdy * sinarg * dargdx * Y0 + K2 * dHdx * dHdy * cosarg * Y1 * dHdx
4377 + K2 * dHdxfHdx * dHdy * cosarg * Y1 + K2 * dHdxfH * dHdydx * cosarg * Y1
4378 - K2 * dHdxfH * dHdy * sinarg * dargdx * Y1 + K2 * dHdxfH * dHdy * cosarg * dY1 * dHdx;
4381 -1.0 * K1 * dHdydy * cosarg * J1 - K1 * dHdy * cosarg * dJ1 * dHdy - K1 * dHdydy * sinarg * Y1
4382 - K1 * dHdy * sinarg * dY1 * dHdy + K2 * dHdxdydy * sinarg * J1 + K2 * dHdxdy * sinarg * dJ1 * dHdy
4383 - K2 * dHdxdydy * cosarg * Y1 - K2 * dHdxdy * cosarg * dY1 * dHdy + K2 * dHdxdy * dHdy * sinarg * J0
4384 + K2 * dHdx * dHdydy * sinarg * J0 - K2 * dHdx * dHdy * sinarg * J1 * dHdy - K2 * dHdxfHdy * dHdy * sinarg * J1
4385 - K2 * dHdxfH * dHdydy * sinarg * J1 - K2 * dHdxfH * dHdy * sinarg * dJ1 * dHdy
4386 - K2 * dHdxdy * dHdy * cosarg * Y0 - K2 * dHdx * dHdydy * cosarg * Y0 + K2 * dHdx * dHdy * cosarg * Y1 * dHdy
4387 + K2 * dHdxfHdy * dHdy * cosarg * Y1 + K2 * dHdxfH * dHdydy * cosarg * Y1
4388 + K2 * dHdxfH * dHdy * cosarg * dY1 * dHdy;
4390 const MFloat pp = -1.0 * rho0 * (dPhidt + u0 * dPhidx);
4393 vars.
u[sample] = dPhidx;
4394 vars.
v[sample] = dPhidy;
4395 vars.
p[sample] = pp;
4397 vars.
u[sample] = dPhidx + u0;
4398 vars.
v[sample] = dPhidy;
4399 vars.
p[sample] = pp + p0;
4400 vars.
rho[sample] = rho0 + pp / (c0 * c0);
4402 transformBackToOriginalCoordinate2D(&vars.
u[sample], &vars.
v[sample]);
4413 transformCoordinatesToFlowDirection3D(&x, &
y, &z);
4418 const MFloat p0 = rho0 * c0 * c0 / m_gamma;
4426 const MFloat beta2 = beta * beta;
4427 const MFloat fbeta2 = 1 / beta2;
4428 const MFloat d = sqrt(x * x + beta * beta * (
y *
y + z * z));
4430 const MFloat d3 = d2 * d;
4431 const MFloat fd2 = 1 / d2;
4432 const MFloat fd3 = 1 / d3;
4433 const MFloat C1 = amplitude / (4 * PI);
4434 const MFloat C2 = k * fbeta2;
4436 const MFloat dargdx = -1.0 * C2 * (x / d - mach);
4437 const MFloat dargdy = -1.0 * k *
y / d;
4438 const MFloat dargdz = -1.0 * k * z / d;
4440 for(
MInt sample = 0; sample < noSamples(); sample++) {
4442 const MFloat time = sample * m_dt;
4443 const MFloat arg = omega * time - k * (d - mach * x) * fbeta2;
4445 const MFloat cosarg = cos(arg);
4446 const MFloat sinarg = sin(arg);
4448 const MFloat dPhidt = -1.0 * C1 * 3 * x *
y * beta2 * fd2 * fd3 * sinarg * omega
4449 - C1 * 3 * k * x *
y * fd2 * fd2 * cosarg * omega + C1 * k * C2 * x *
y * fd3 * sinarg * omega
4450 + C1 * mach * k *
y * fd3 * cosarg * omega - C1 * mach * k * C2 *
y * fd2 * sinarg * omega;
4453 C1 * 3 *
y * beta2 * (fd2 * fd3 - 5 * x * x * fd2 * fd2 * fd3) * cosarg
4454 - C1 * 3 * x *
y * beta2 * fd2 * fd3 * sinarg * dargdx
4455 - C1 * 3 * k *
y * (fd2 * fd2 - 4 * x * x * fd3 * fd3) * sinarg
4456 - C1 * 3 * k * x *
y * fd2 * fd2 * cosarg * dargdx - C1 * k * C2 *
y * (fd3 - 3 * x * x * fd3 * fd2) * cosarg
4457 + C1 * k * C2 * x *
y * fd3 * sinarg * dargdx - C1 * mach * k *
y * 3 * x * fd3 * fd2 * sinarg
4458 + C1 * mach * k *
y * fd3 * cosarg * dargdx - C1 * mach * k * C2 *
y * 2 * x * fd2 * fd2 * cosarg
4459 - C1 * mach * k * C2 *
y * fd2 * sinarg * dargdx;
4462 C1 * 3 * x * beta2 * (fd2 * fd3 - 5 * beta2 *
y *
y * fd2 * fd2 * fd3) * cosarg
4463 - C1 * 3 * x *
y * beta * beta * fd3 * fd2 * sinarg * dargdy
4464 - C1 * 3 * k * x * (fd2 * fd2 - 4 * beta2 *
y *
y * fd3 * fd3) * sinarg
4465 - C1 * 3 * k * x *
y * fd2 * fd2 * cosarg * dargdy
4466 - C1 * k * C2 * x * (fd3 - 3 * beta2 *
y *
y * fd2 * fd3) * cosarg + C1 * k * C2 * x *
y * fd3 * sinarg * dargdy
4467 + C1 * mach * k * (fd3 - 3 * beta2 *
y *
y * fd2 * fd3) * sinarg + C1 * mach * k *
y * fd3 * cosarg * dargdy
4468 + C1 * mach * k * C2 * (fd2 - 2 * beta2 *
y *
y * fd2 * fd2) * cosarg
4469 - C1 * mach * k * C2 *
y * fd2 * sinarg * dargdy;
4472 -1.0 * C1 * 15 * x *
y * z * beta2 * beta2 * fd2 * fd2 * fd3 * cosarg
4473 - C1 * 3 * x *
y * beta2 * fd2 * fd3 * sinarg * dargdz + C1 * 12 * k * x *
y * z * beta2 * fd3 * fd3 * sinarg
4474 - C1 * 3 * k * x *
y * fd2 * fd2 * cosarg * dargdz + C1 * 3 * k * k * x *
y * z * fd2 * fd3 * cosarg
4475 + C1 * k * C2 * x *
y * fd3 * sinarg * dargdz - C1 * 3 * mach * k *
y * z * beta2 * fd2 * fd3 * sinarg
4476 + C1 * mach * k *
y * fd3 * cosarg * dargdz - C1 * 2 * mach * k * k *
y * z * fd2 * fd2 * cosarg
4477 - C1 * mach * k * C2 *
y * fd2 * sinarg * dargdz;
4479 const MFloat pp = -1.0 * rho0 * (dPhidt + u0 * dPhidx);
4482 vars.
u[sample] = dPhidx;
4483 vars.
v[sample] = dPhidy;
4484 vars.
w[sample] = dPhidz;
4485 vars.
p[sample] = pp;
4487 vars.
u[sample] = dPhidx + u0;
4488 vars.
v[sample] = dPhidy;
4489 vars.
w[sample] = dPhidz;
4490 vars.
p[sample] = pp + p0;
4491 vars.
rho[sample] = rho0 + pp / (c0 * c0);
4493 transformBackToOriginalCoordinate3D(&vars.
u[sample], &vars.
v[sample], &vars.
w[sample]);
4508 const MFloat x = coord[0];
4510 constexpr MFloat cs = 1.0;
4513 const MFloat fac = cs / (2.0 * PI);
4514 for(
MInt sample = 0; sample < noSamples(); sample++) {
4515 const MFloat t = sample * m_dt;
4516 const MFloat xRel = x - param.
Ma * cs * t;
4519 const MFloat theta = atan2(yRel, xRel);
4520 const MFloat utheta = fac * r * exp(0.5 * (1 - r * r));
4521 const MFloat up = utheta * sin(theta);
4522 const MFloat vp = -utheta * cos(theta);
4523 const MFloat pp = pow(1 + (m_gamma - 1) * exp(1 - r * r) / (8.0 * PI * PI), m_gamma / (m_gamma - 1.0)) - 1.0;
4525 vars.
u[sample] = up;
4526 vars.
v[sample] = vp;
4527 vars.
p[sample] = pp;
4529 vars.
u[sample] = param.
Ma * cs + up;
4530 vars.
v[sample] = vp;
4531 vars.
p[sample] = pp;
4532 if(vars.
rho !=
nullptr) vars.
rho[sample] = rho0 * pow((1.0 + pp), 1.0 / m_gamma);
4549 const MInt noSym = m_symBc.size();
4550 if(noSym == 0)
return;
4551 TERMM_IF_COND(noSym > 1,
"WARNING: only implemented for one symmetry BC so far");
4552 auto transform = [](std::array<MFloat, nDim>& coord_,
const AcaSymBc& bc) {
4553 const MFloat coordTimesNormal = std::inner_product(&coord_[0], &coord_[nDim], &bc.normal[0], .0);
4554 for(
MInt d = 0; d < nDim; d++) {
4555 coord_[d] += -2 * coordTimesNormal * bc.normal[d] + 2 * bc.origin[d];
4560 std::array<MFloat, nDim> symCoord = coord;
4561 transform(symCoord, m_symBc[0]);
4563 calcSurfaceIntegralsForObserver(symCoord, complexVariables_.
data());
4564 for(
MInt i = 0; i < noSamples(); i++) {
4565 p_complexVariables[2 * i + 0] += complexVariables_[2 * i + 0];
4566 p_complexVariables[2 * i + 1] += complexVariables_[2 * i + 1];
4574 RECORD_TIMER_START(m_timers[Timers::InitObservers]);
4576 MInt noGlobalObservers = 0;
4577 printMessage(
"- Init observer points");
4578 std::stringstream ss;
4580 if(m_generateObservers) {
4581 MString observerGenerationType =
"CIRCLE";
4582 observerGenerationType =
4583 Context::getSolverProperty<MString>(
"observerGenerationType", m_solverId, AT_, &observerGenerationType);
4593 noGlobalObservers = 100;
4594 noGlobalObservers = Context::getSolverProperty<MInt>(
"noObservers", m_solverId, AT_, &noGlobalObservers);
4595 if(noGlobalObservers < 1) {
4596 TERMM(1,
"The number of Observers cannot be lower than 1!");
4598 m_globalObserverCoordinates.resize(noGlobalObservers * nDim);
4601 <<
"Generate " << noGlobalObservers <<
" observer with mode " << observerGenerationType;
4603 if(observerGenerationType ==
MString(
"CIRCLE")) {
4606 const MFloat step_rad = 2 * PI /
static_cast<MFloat>(noGlobalObservers);
4616 radius = Context::getSolverProperty<MFloat>(
"observerRadius", m_solverId, AT_, &radius);
4618 TERMM(1,
"The radius cannot be equal or lower than 0!");
4621 for(
MInt i = 0; i < noGlobalObservers; i++) {
4623 const MFloat x = radius *
cos(i * step_rad);
4624 const MFloat y = radius * sin(i * step_rad);
4626 m_globalObserverCoordinates[i * nDim] = x;
4627 m_globalObserverCoordinates[i * nDim + 1] =
y;
4628 if constexpr(nDim == 3) {
4629 m_globalObserverCoordinates[i * nDim + 2] = 0.0;
4633 }
else if(observerGenerationType ==
MString(
"PLANE")) {
4636 if constexpr(nDim > 2) {
4637 normalDir = Context::getSolverProperty<MInt>(
"observerGenerationPlaneNormal", m_solverId, AT_, &normalDir);
4640 for(
MInt i = 0; i < 4; i++) {
4641 dim[i] = Context::getSolverProperty<MFloat>(
"observerGenerationPlaneDimension", m_solverId, AT_, i);
4645 const MFloat l[2] = {dim[2] - dim[0], dim[3] - dim[1]};
4647 nx[0] = sqrt(noGlobalObservers * l[0] / l[1]);
4648 nx[1] = noGlobalObservers / nx[0];
4649 const MFloat dx[2] = {l[0] / nx[0], l[1] / nx[1]};
4651 for(
MInt i = 0; i < noGlobalObservers; i++) {
4652 const MInt j = i % nx[0];
4653 const MInt k = i / nx[0];
4654 m_globalObserverCoordinates[i * nDim + 0] = dim[0] + dx[0] * j;
4655 m_globalObserverCoordinates[i * nDim + 1] = dim[1] + dx[1] * k;
4658 ss <<
" is not possible.";
4660 }
else if(m_observerFileName !=
"") {
4662 if(domainId() == 0) {
4665 if(noGlobalObservers < 1) {
4666 TERMM(1,
"Error: no observer points found in file " + m_observerFileName);
4669 <<
"Read " << noGlobalObservers <<
" observer points from file " << m_observerFileName;
4676 if(domainId() != 0) {
4677 m_globalObserverCoordinates.resize(noGlobalObservers * nDim);
4682 mpiComm(), AT_,
"coordinates");
4684 TERMM(1,
"Error: no observer file name specified and generation of observers not enabled.");
4687 m_noGlobalObservers = noGlobalObservers;
4690 const MInt noObsVars = 1;
4691 const MInt noObsComplexVars = 1;
4694 m_observerData.setNoVars(noObsVars);
4695 m_observerData.setNoComplexVars(noObsComplexVars);
4696 m_observerData.setNoSamples(m_noSamples);
4699 distributeObservers();
4702 m_observerData.reset(noObservers());
4705 for(
MInt localObserverId = 0; localObserverId < noObservers(); localObserverId++) {
4706 m_observerData.append();
4707 const MInt globalObserverId = a_globalObserverId(localObserverId);
4708 MFloat* obsCoord = &m_observerData.observerCoordinates(localObserverId);
4709 for(
MInt d = 0; d < nDim; d++) {
4710 obsCoord[d] = m_globalObserverCoordinates[globalObserverId * nDim + d];
4714 printMessage(ss.str());
4715 RECORD_TIMER_STOP(m_timers[Timers::InitObservers]);
4726 std::stringstream ss;
4727 printMessage(
"- Init conversion factors");
4728 switch(m_acaResultsNondimMode) {
4730 case NONDIMSTAGNATION: {
4731 const MFloat constant = 1.0 + (m_gamma - 1.0) * 0.5 * m_MaDim * m_MaDim;
4732 m_input2Aca.length = 1.0;
4733 m_input2Aca.density = pow(constant, (1.0 / (m_gamma - 1.0)));
4734 m_input2Aca.velocity = sqrt(constant);
4735 m_input2Aca.time = m_input2Aca.length / m_input2Aca.velocity;
4736 m_input2Aca.pressure = pow(constant, (m_gamma / (m_gamma - 1.0)));
4737 constexpr MFloat HzToRad = 2.0 * PI;
4738 m_input2Aca.frequency = HzToRad / m_input2Aca.time;
4740 if(m_acaResultsNondimMode == NONDIMACA) {
4742 }
else if(m_acaResultsNondimMode == NONDIMSTAGNATION) {
4743 m_aca2Output.length = 1.0 / m_input2Aca.length;
4744 m_aca2Output.density = 1.0 / m_input2Aca.density;
4745 m_aca2Output.velocity = 1.0 / m_input2Aca.velocity;
4746 m_aca2Output.time = 1.0 / m_input2Aca.time;
4747 m_aca2Output.pressure = 1.0 / m_input2Aca.pressure;
4748 m_aca2Output.frequency = 1.0 / m_input2Aca.frequency;
4757 m_input2Aca.density = 1.0;
4758 m_input2Aca.time = 1.0 / m_MaDim;
4762 m_input2Aca.length = std::numeric_limits<double>::quiet_NaN();
4763 m_input2Aca.velocity = F1BCS;
4764 m_input2Aca.pressure = m_input2Aca.density *
POW2(m_input2Aca.velocity);
4768 TERMM(1,
"Invalid acaResultsNondimMode: '" + std::to_string(m_acaResultsNondimMode) +
"'");
4771 ss <<
" MaDim = " << m_MaDim << std::endl;
4772 ss <<
" Conversion factors input2Aca | aca2Output:" << std::endl;
4773 ss <<
" velocity = " << m_input2Aca.velocity <<
" | " << m_aca2Output.velocity << std::endl;
4774 ss <<
" pressure = " << m_input2Aca.pressure <<
" | " << m_aca2Output.pressure << std::endl;
4775 ss <<
" density = " << m_input2Aca.density <<
" | " << m_aca2Output.density << std::endl;
4776 ss <<
" time = " << m_input2Aca.time <<
" | " << m_aca2Output.time << std::endl;
4777 ss <<
" frequency= " << m_input2Aca.frequency <<
" | " << m_aca2Output.frequency;
4778 printMessage(ss.str());
4787 m_log <<
"Change dimensions from stagnation to freestream state" << std::endl;
4790 const MInt dummy_u = (m_acousticMethod ==
FWH_METHOD) ? FWH::U : FWH_APE::UP;
4791 const MInt dummy_v = (m_acousticMethod ==
FWH_METHOD) ? FWH::V : FWH_APE::VP;
4792 const MInt dummy_w = (m_acousticMethod ==
FWH_METHOD) ? FWH::W : FWH_APE::WP;
4793 const MInt dummy_p = (m_acousticMethod ==
FWH_METHOD) ? FWH::P : FWH_APE::PP;
4796 for(
MInt seg = 0; seg < totalNoSurfaceElements(); seg++) {
4797 for(
MInt t = 0; t < noSamples(); t++) {
4798 m_surfaceData.variables(seg, dummy_u, t) *= m_input2Aca.velocity;
4799 m_surfaceData.variables(seg, dummy_v, t) *= m_input2Aca.velocity;
4800 if constexpr(nDim == 3) m_surfaceData.variables(seg, dummy_w, t) *= m_input2Aca.velocity;
4801 m_surfaceData.variables(seg, dummy_p, t) *= m_input2Aca.pressure;
4803 m_surfaceData.variables(seg, FWH::RHO, t) *= m_input2Aca.density;
4809 for(
MInt i = 0; i < noSamples(); i++) {
4810 m_times[i] *= m_input2Aca.time;
4817 printMessage(
"- Change dimension of observer data");
4818 if(
approx(m_aca2Output.frequency, 1.0, MFloatEps)) {
4819 for(
MInt i = 0; i < noSamples(); i++) {
4820 m_frequencies[i] *= m_aca2Output.frequency;
4823 if(
approx(m_aca2Output.pressure, 1.0, MFloatEps)) {
4824 for(
MInt obs = 0; obs < noObservers(); obs++) {
4825 for(
MInt t = 0; t < noSamples(); t++) {
4826 m_observerData.variables(obs, 0, t) *= m_aca2Output.pressure;
4829 for(
MInt obs = 0; obs < noObservers(); obs++) {
4830 for(
MInt f = 0; f < noSamples(); f++) {
4831 m_observerData.complexVariables(obs, 0, f, 0) *= m_aca2Output.pressure;
4832 m_observerData.complexVariables(obs, 0, f, 1) *= m_aca2Output.pressure;
4836 if(
approx(m_aca2Output.time, 1.0, MFloatEps)) {
4837 m_dt *= m_aca2Output.time;
4838 for(
MInt i = 0; i < noSamples(); i++) {
4839 m_times[i] *= m_aca2Output.time;
4847 m_dt = (m_times[noSamples() - 1] - m_times[0]) / (
static_cast<MFloat>(noSamples() - 1));
4850 for(
MInt i = 1; i < noSamples(); i++) {
4851 const MFloat delta = m_times[i] - m_times[i - 1];
4852 const MFloat relErr = std::fabs(m_dt - delta) / m_dt;
4853 if(relErr > 1e-10) {
4854 std::ostringstream err;
4855 err << std::setprecision(12) << std::scientific <<
"Error: time step size mismatch; m_dt = " << m_dt
4856 <<
"; delta = " << delta <<
"; relErr = " << relErr <<
"; i = " << i <<
"; times[i] = " << m_times[i]
4857 <<
"; times[i-1] = " << m_times[i - 1];
4858 TERMM(1, err.str());
4866 MInt numSampl = noSamples();
4868 m_FastFourier =
false;
4871 while(numSampl > 1) {
4875 if(prove == noSamples()) {
4876 m_FastFourier =
true;
4877 m_log <<
" >> NOTE: Number of samples is a potency of 2. Thus Fast Fourier Transform can "
4878 "be used, if not defined otherwise (transformationType = 0 for DFT)."
4881 m_FastFourier =
false;
4882 m_transformationType = 0;
4883 m_log <<
" >> NOTE: Number of samples is not a potency of 2. Thus Fast Fourier Transform "
4884 "cannot be used, no matter what."
4893 TERMM(1,
"TODO check this function");
4894 if(domainId() == 0) {
4895 std::cout <<
"8. Starting accuarcy calculation." << std::endl;
4899 const MInt observer = globalNoObservers();
4901 noSegs = Context::getSolverProperty<MFloat>(
"noSegments_" + std::to_string(0), m_solverId, AT_, 0);
4903 noSegs = Context::getSolverProperty<MFloat>(
"noSegments_" + std::to_string(0), m_solverId, AT_, 1);
4906 MFloat box_size = 2.0 * 2;
4907 box_size = Context::getSolverProperty<MFloat>(
"surfaceCoordinates_" + std::to_string(0), m_solverId, AT_, 0);
4908 box_size = 2.0 * std::fabs(box_size);
4913 noPeriods = Context::getSolverProperty<MFloat>(
"noPeriods", m_solverId, AT_, &noPeriods);
4915 MFloat omega_factor = 4.0;
4916 omega_factor = Context::getSolverProperty<MFloat>(
"omega_factor", m_solverId, AT_, &omega_factor);
4918 MFloat Delta_Seg = box_size / noSegs;
4919 if constexpr(nDim == 3) {
4920 Delta_Seg *= Delta_Seg;
4924 m_lambdaZero = 2.0 / m_omega_factor;
4925 m_lambdaMach = 2.0 * (1.0 - m_Ma) / m_omega_factor;
4928 const MFloat R_min = noSamples() * m_lambdaMach / (noPeriods * m_lambdaZero);
4929 const MFloat Z = Delta_Seg / R_min;
4930 const MFloat Y = Delta_Seg / m_lambdaMach;
4933 std::vector<MFloat> Analytic_EvenPeriods(globalNoObservers() * noSamples() / 2 * 5, 0.0);
4934 std::vector<MFloat> Analytic_EvenPeriods_Amplitude(globalNoObservers() * noSamples() / 2, 0.0);
4935 std::vector<MFloat> Analytic_EvenPeriods_Frequencies(globalNoObservers() * noSamples() / 2, 0.0);
4936 std::vector<MFloat> RMS_EvenPeriods(globalNoObservers(), 0.0);
4938 const MInt EP_Samples = 64;
4939 if(noSamples() == EP_Samples) {
4942 MString Name =
"./test/AnalyticSolution_ForEvenPeriods.dat";
4943 file.open(Name, std::ios::in);
4945 MFloat i1, i2, i3, i4, i5;
4946 if(file.is_open()) {
4947 while(getline(file, line)) {
4948 std::stringstream linebuffer(line);
4949 linebuffer >> i1 >> i2 >> i3 >> i4 >> i5;
4950 Analytic_EvenPeriods[5 * count] = i1;
4951 Analytic_EvenPeriods[5 * count + 1] = i2;
4952 Analytic_EvenPeriods[5 * count + 2] = i3;
4953 Analytic_EvenPeriods[5 * count + 3] = i4;
4954 Analytic_EvenPeriods[5 * count + 4] = i5;
4956 Analytic_EvenPeriods_Amplitude[count] = i4;
4957 Analytic_EvenPeriods_Frequencies[count] = i1;
4961 std::cerr <<
"Fehler beim Öffnen der Datei: " << Name <<
"." << std::endl;
4964 std::ifstream file1;
4966 MString Name1 =
"./test/RMSEvenPeriods.dat";
4967 file1.open(Name1, std::ios::in);
4970 if(file1.is_open()) {
4971 while(getline(file1, line1)) {
4972 std::stringstream linebuffer(line1);
4973 linebuffer >> i_1 >> i_2;
4974 RMS_EvenPeriods[count1] = i_2;
4978 std::cerr <<
"Fehler beim Öffnen der Datei: " << Name1 <<
"." << std::endl;
4982 std::vector<MFloat> p_rms(globalNoObservers(), 0.0);
4984 for(
MInt obs = 0; obs < globalNoObservers(); obs++) {
4985 for(
MInt t = 0; t < noSamples(); t++) {
4986 p_rms[obs] += m_observerData.variables(obs, 0, t) * m_observerData.variables(obs, 0, t);
4988 p_rms[obs] = sqrt(p_rms[obs] / noSamples());
4994 MFloat diff_normalized = 0.0;
4995 MFloat diff_2_normalized = 0.0;
4996 MFloat sum_diff_sq = 0.0;
4997 MFloat sum_diff_2_sq = 0.0;
4998 MFloat sum_analytic_sq = 0.0;
4999 MFloat sum_a_ep_sq = 0.0;
5000 std::vector<MFloat> diff(observer, 0.0);
5001 std::vector<MFloat> diff_2(observer, 0.0);
5002 for(
MInt i = 0; i < observer; i++) {
5003 diff[i] = std::fabs(m_rmsP_Analytic[i] - p_rms[i]);
5004 sum_diff_sq += diff[i] * diff[i];
5005 sum_analytic_sq += m_rmsP_Analytic[i];
5006 diff_normalized = diff[i] / m_rmsP_Analytic[i] * 100;
5009 if(noSamples() == EP_Samples) {
5010 diff_2[i] = std::fabs(RMS_EvenPeriods[i] - p_rms[i]);
5011 sum_diff_2_sq += diff_2[i] * diff_2[i];
5012 sum_a_ep_sq += RMS_EvenPeriods[i];
5013 diff_2_normalized = diff_2[i] / RMS_EvenPeriods[i] * 100;
5015 if(diff_normalized >= max_diff) {
5016 if(m_rmsP_Analytic[i] > 10e-13) {
5017 max_diff = diff_normalized;
5019 if(EP_Samples == 64) {
5020 max_diff_2 = diff_2_normalized;
5024 const MFloat value_top = sqrt(sum_diff_sq / globalNoObservers());
5025 const MFloat value_bot = sum_analytic_sq / globalNoObservers();
5026 const MFloat Q_C = 100 * value_top / value_bot;
5029 MFloat Q_C_UnevenPeriods = 0.0;
5030 if(noSamples() == EP_Samples) {
5031 const MFloat value_top_UP = sqrt(sum_diff_2_sq / globalNoObservers());
5032 const MFloat value_bot_UP = sum_a_ep_sq / globalNoObservers();
5033 Q_C_UnevenPeriods = 100 * value_top_UP / value_bot_UP;
5037 std::vector<MFloat> diff_Ampl(globalNoObservers() * noSamples(), 0.0);
5038 std::vector<MFloat> max_Ampl(globalNoObservers(), 0.0);
5039 std::vector<MFloat> max_Ampl_2(globalNoObservers(), 0.0);
5040 std::vector<MFloat> max_Ampl_a(globalNoObservers(), 0.0);
5041 std::vector<MFloat> max_Ampl_A_EvenPeriods(globalNoObservers(), 0.0);
5042 std::vector<MFloat> max_Ampl_Diff(globalNoObservers(), 0.0);
5043 std::vector<MFloat> max_Ampl_Diff_2(globalNoObservers(), 0.0);
5044 std::vector<MFloat> max_Ampl_Diff_normalized(globalNoObservers(), 0.0);
5045 std::vector<MFloat> max_Ampl_Diff_normalized_2(globalNoObservers(), 0.0);
5046 std::vector<MFloat> freq_At_Max_Ampl(globalNoObservers(), 0.0);
5047 std::vector<MFloat> freq_At_Max_Ampl_2(globalNoObservers(), 0.0);
5048 std::vector<MFloat> freq_At_Max_Ampl_a(globalNoObservers(), 0.0);
5049 std::vector<MFloat> freq_At_Max_Ampl_A_EvenPeriods(globalNoObservers(), 0.0);
5050 std::vector<MFloat> freq_Diff(globalNoObservers(), 0.0);
5051 std::vector<MFloat> freq_Diff_2(globalNoObservers(), 0.0);
5052 std::vector<MFloat> freq_Diff_normalized(globalNoObservers(), 0.0);
5053 std::vector<MFloat> freq_Diff_normalized_2(globalNoObservers(), 0.0);
5054 std::vector<MFloat> mean_Ampl_analytic(globalNoObservers(), 0.0);
5055 std::vector<MFloat> sum_Diff_Amplitudes_UnevenPeriods(globalNoObservers(), 0.0);
5056 std::vector<MFloat> SideLobes(globalNoObservers(), 0.0);
5057 MFloat sum_max_Ampl_Diff_normalized = 0.0;
5058 MFloat sum_freq_Diff_normalized = 0.0;
5059 MFloat sum_max_Ampl_Diff_normalized_2 = 0.0;
5060 MFloat sum_freq_Diff_normalized_2 = 0.0;
5061 MFloat sum_SideLobes = 0.0;
5062 MFloat Amplitude_EvenPeriods = 0.0;
5063 for(
MInt obs = 0; obs < globalNoObservers(); obs++) {
5064 for(
MInt i = 1; i < noSamples() / 2 + 1; i++) {
5065 const MFloat real_c = m_observerData.complexVariables(obs, 0, i, 0);
5066 const MFloat imag_c = m_observerData.complexVariables(obs, 0, i, 1);
5067 const MFloat real_a = m_Analyticfreq[obs * noSamples() * 2 + 2 * i];
5068 const MFloat imag_a = m_Analyticfreq[obs * noSamples() * 2 + 2 * i + 1];
5069 const MFloat Amplitude_c = 2 * sqrt(real_c * real_c + imag_c * imag_c);
5070 const MFloat Amplitude_a = 2 * sqrt(real_a * real_a + imag_a * imag_a);
5072 if(noSamples() == EP_Samples) {
5073 Amplitude_EvenPeriods = Analytic_EvenPeriods_Amplitude[obs * noSamples() / 2 + (i - 1)];
5076 if(Amplitude_EvenPeriods > max_Ampl_A_EvenPeriods[obs]) {
5077 freq_At_Max_Ampl_A_EvenPeriods[obs] = Analytic_EvenPeriods_Frequencies[obs * noSamples() / 2 + (i - 1)];
5078 max_Ampl_A_EvenPeriods[obs] = Amplitude_EvenPeriods;
5083 if(Amplitude_a > max_Ampl_a[obs]) {
5084 freq_At_Max_Ampl_a[obs] = m_frequencies[i];
5085 max_Ampl_a[obs] = Amplitude_a;
5089 if(Amplitude_c > max_Ampl[obs]) {
5090 freq_At_Max_Ampl[obs] = m_frequencies[i];
5091 max_Ampl[obs] = Amplitude_c;
5095 SideLobes[obs] += std::fabs(Amplitude_EvenPeriods - Amplitude_c);
5099 max_Ampl_Diff[obs] = std::fabs(max_Ampl_a[obs] - max_Ampl[obs]);
5100 freq_Diff[obs] = std::fabs(freq_At_Max_Ampl[obs] - freq_At_Max_Ampl_a[obs]);
5102 max_Ampl_Diff_normalized[obs] = max_Ampl_Diff[obs] / max_Ampl_a[obs];
5103 freq_Diff_normalized[obs] = freq_Diff[obs] / freq_At_Max_Ampl_a[obs];
5105 sum_max_Ampl_Diff_normalized += max_Ampl_Diff_normalized[obs];
5106 sum_freq_Diff_normalized += freq_Diff_normalized[obs];
5108 if(noSamples() == EP_Samples) {
5109 max_Ampl_Diff_2[obs] = std::fabs(max_Ampl_A_EvenPeriods[obs] - max_Ampl[obs]);
5110 freq_Diff_2[obs] = std::fabs(freq_At_Max_Ampl[obs] - freq_At_Max_Ampl_A_EvenPeriods[obs]);
5111 sum_SideLobes += (SideLobes[obs] / (noSamples() / 2));
5113 max_Ampl_Diff_normalized_2[obs] = max_Ampl_Diff_2[obs] / max_Ampl_A_EvenPeriods[obs];
5114 freq_Diff_normalized_2[obs] = freq_Diff_2[obs] / freq_At_Max_Ampl_A_EvenPeriods[obs];
5117 sum_max_Ampl_Diff_normalized_2 += max_Ampl_Diff_normalized_2[obs];
5118 sum_freq_Diff_normalized_2 += freq_Diff_normalized_2[obs];
5121 const MFloat Ampl_aver_Diff = 100 * sum_max_Ampl_Diff_normalized / globalNoObservers();
5122 const MFloat Freq_aver_Diff = 100 * sum_freq_Diff_normalized / globalNoObservers();
5126 MFloat Freq_aver_Diff_UnevenPeriods = 0.0;
5127 MFloat Ampl_aver_Diff_UnevenPeriods = 0.0;
5128 if(noSamples() == EP_Samples) {
5129 Ampl_aver_Diff_UnevenPeriods = 100 * sum_max_Ampl_Diff_normalized_2 / globalNoObservers();
5130 Freq_aver_Diff_UnevenPeriods = 100 * sum_freq_Diff_normalized_2 / globalNoObservers();
5131 Side_Lobes = 100 * sum_SideLobes / globalNoObservers();
5134 if(Freq_aver_Diff_UnevenPeriods < 1e-10) {
5135 Freq_aver_Diff_UnevenPeriods = 0.0;
5140 MString fileFreq =
"./FreqComparison/maxFreqCompare_";
5141 fileFreq += std::to_string(noSegs);
5143 std::ofstream outfile;
5144 outfile.open(fileFreq);
5145 outfile << Y <<
" " << Freq_aver_Diff <<
" " << Ampl_aver_Diff <<
" " << Side_Lobes << std::endl;
5149 MString fileF =
"./FreqComparison/maxFreqCompareSamples_";
5150 fileF += std::to_string(noSamples());
5152 outfile.open(fileF);
5153 outfile << R_min <<
" " << Freq_aver_Diff <<
" " << Ampl_aver_Diff <<
" " << Side_Lobes << std::endl;
5157 if(noSamples() == EP_Samples) {
5159 MString fileFr =
"./FreqComparison/maxFreqComparePeriods_";
5160 fileFr += std::to_string(noPeriods);
5162 outfile.open(fileFr);
5163 outfile << noPeriods <<
" " << Freq_aver_Diff_UnevenPeriods <<
" " << Ampl_aver_Diff_UnevenPeriods <<
" "
5164 << Side_Lobes <<
" " << Ampl_aver_Diff << std::endl;
5169 MString ofile =
"./Diff_seg/accuracy_";
5170 ofile += std::to_string(noSegs);
5172 outfile.open(ofile);
5173 outfile << Y <<
" " << Q_C <<
" " << max_diff <<
" " << Z <<
" " << Side_Lobes << std::endl;
5177 MString fileName =
"./Diff_Samples/accuracy_";
5178 fileName += std::to_string(noSamples());
5180 outfile.open(fileName);
5181 outfile << R_min <<
" " << Q_C <<
" " << max_diff <<
" " << Side_Lobes << std::endl;
5184 if(noSamples() == EP_Samples) {
5186 MString fileN =
"./Diff_Periods/accuracy_";
5187 fileN += std::to_string(noPeriods);
5189 outfile.open(fileN);
5190 outfile << noPeriods <<
" " << Q_C_UnevenPeriods <<
" " << max_diff_2 <<
" " << Side_Lobes << std::endl;
5195 MString fileNa =
"./Diff_omega/accuracy_";
5196 fileNa += std::to_string(omega_factor);
5198 outfile.open(fileNa);
5199 outfile << omega_factor <<
" " << Q_C <<
" " << max_diff <<
" " << Side_Lobes << std::endl;
5202 std::cout <<
"8. Accuarcy calculation finished." << std::endl;
ACA post processing class for overall sound pressure calculation.
ACA post processing class for absoulte pressure calculation.
ACA post processing class for calculation of root mean square pressure.
ACA post processing class for sound pressure level calculation.
Acoustic extrapolation of near/near-far field data to the true far-field using an integral method (cu...
AcaSolver(const MInt solverId, const MPI_Comm comm)
void transformCoordinatesToFlowDirection3D(MFloat *const fX, MFloat *const fY, MFloat *const fZ)
void readNoSegmentsAndSamples(const MInt surfaceId, MInt &noSegments, MInt &noSamples, MString &inputFileName)
Read the number of segments and samples for the given surface id.
void generateSurfaceData()
Generation of analytical input data.
void loadObserverSignals()
Load the observer signals from a file for further postprocessing.
void applySymmetryBc(const std::array< MFloat, nDim > coord, MFloat *const p_complexVariables)
Symmetry boundary condition.
void initConversionFactors()
Initialize the conversion factors required to convert between.
void interpolateFromSourceToObserverTime(const MFloat distMinObservers, MFloat *const p_perturbedFWH)
Interpolate the observer pressure signal from source time to observer time using the advanced time ap...
void storeSurfaceData(const MInt surfaceId)
Store the surface data. Note: used for validating the data input and for the output of generated data...
void calcObsPressureAnalytic()
void genQuadrupoleAnalytic2D(const MFloat *coord, const SourceParameters ¶m, SourceVars &vars)
Quadrupole.
void combineSurfaceIntegralsInTime(const MInt globalObserverId, MFloat *const p_perturbedFWH)
Combine the FWH surface integrals of the time domain method of all ranks.
void genDipoleAnalytic2D(const MFloat *coord, const SourceParameters ¶m, SourceVars &vars)
Dipole.
void checkNoSamplesPotencyOfTwo()
void changeDimensionsObserverData()
void calcSurfaceIntegralsAtSourceTime(const MInt globalObserverId)
Calculate the FWH surface integrals for a certain observer using the time-domain formulation.
MFloat calcTimeDerivative(const MInt segmentId, const MInt varId, const MInt tau)
void transformBackToOriginalCoordinate2D(MFloat *const fX, MFloat *const fY)
void genQuadrupoleAnalytic3D(const MFloat *coord, const SourceParameters ¶m, SourceVars &vars)
3D quadrupole analytic
void setNumericalProperties()
Read/set all numerical properties.
void generateSurfaceCircle(const MInt sId)
Generate a circular surface in 2D.
void distributeObservers()
Distribute observers on all ranks.
void calcSourceTermsFwhTime(const MInt segmentId)
Calculate FWH source terms for the time domain FWH formulation Reference: [1] Brentner,...
void genDipoleAnalytic3D(const MFloat *coord, const SourceParameters ¶m, SourceVars &vars)
3D dipole analytic
void postprocessObserverSignals()
Postprocessing of observer signals.
void calcFrequencies()
Calculate the frequencies.
void genMonopoleAnalytic3D(const MFloat *coord, const SourceParameters ¶m, SourceVars &vars)
3D analytic monopole generation
void calcSurfaceIntegralsForObserver(const std::array< MFloat, nDim > coord, MFloat *const p_complexVariables)
Calculate the FWH surface integrals for a certain observer location.
MBool solutionStep() override
Main compute method to perform acoustic far-field prediction.
void calculateObserverInFrequency()
Calculate the observer signals in frequency domain.
void windowAndTransformSources()
Apply window function to source terms and transform into frequency domain.
void generateSurfacePlane(const MInt sId)
Generate a surface plane (Cartesian).
void DiscreteFourierTransform(MFloat *dataArray, const MInt size, const MInt dir, MFloat *result, const MBool inPlace)
Discrete Fourier transform (computational expensive O(n^2) instead of O(n*logn) compared to FFT)
void generateSurfaces()
Generation of surfaces.
void saveObserverSignals()
Save the observer signals. Save observer data: 1. pressure data in time domain and 2....
void calcSourceTerms()
Main compute functions.
void genMonopoleAnalytic2D(const MFloat *coord, const SourceParameters ¶m, SourceVars &vars)
Monopole.
void changeDimensionsSurfaceData()
Change of the non-dimensionalization of input data from the stagnation state (0) non-dim....
void calcMinDistanceForObservers(MFloat *const distMinObservers)
Find the minimum distance from the surface elements to the observers.
void readSurfaceData()
Read the surface data (or generate for an analytical case)
void readSurfaceDataFromFile(const MInt surfaceId)
Read the data for one surface.
MInt readNoSegmentsSurfaceGeneration(const MInt sId, std::vector< MInt > &noSegments, const MInt lengthCheck=-1)
Determine the number of segments to be generated for a surface, returns the total number of segments ...
void initTimers()
Initialize solver timers.
void initParallelization()
Initialize parallelization (distribution of surface segments on all domains)
void initSolver() override
Initialize solver.
void averageTimer()
Average all FWH timer over participating ranks.
void initObserverPoints()
Initialize observer points.
void genVortexConvectionAnalytic2D(const MFloat *obsCoord, const SourceParameters &m_sourceParameters, SourceVars &sourceVars)
Inviscid vortex convection.
void backtransformObservers()
Backtransform the observer frequency signals into the time domain.
void windowAndTransformObservers()
Apply window function to the observer time series data and transform it into frequency domain.
void transformCoordinatesToFlowDirection2D(MFloat *const fX, MFloat *const fY)
Coordinate transformation.
void calcWindowFactors(MFloat *const p_window)
Compute the window function factors and the normalization factor.
void setInputOutputProperties()
Read/set all I/O related properties.
void FastFourierTransform(MFloat *dataArray, const MInt size, const MInt dir, MFloat *result, const MBool inPlace)
Functions for Fourier Transformation.
void calculateObserverInTime()
Calculate the observer signals in time domain.
MString getSurfaceDataFileName(const MInt surfaceId, const MInt fileNo)
IO methods.
void transformBackToOriginalCoordinate3D(MFloat *const fX, MFloat *const fY, MFloat *const fZ)
void combineSurfaceIntegrals(const MInt globalObserverId, MFloat *const p_complexVariables)
Combine the FWH surface integrals of all ranks.
void calcSourceTermsFwhFreq(const MInt segmentId)
Source term compute kernels.
static MInt propertyLength(const MString &name, MInt solverId=m_noSolvers)
Returns the number of elements of a property.
static void assertPropertyLength(const MString &name, const MInt length, const MInt solverId=m_noSolvers)
Assert that the length of a property matches the given length.
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
This class is a ScratchSpace.
void fill(T val)
fill the scratch with a given value
Parent class of all solvers This class is the base for all solvers. I.e. all solver class (e....
MInt string2enum(MString theString)
This global function translates strings in their corresponding enum values (integer values)....
MInt loadPointCoordinatesFromFile(const MString inputFileName, const MInt nDim, std::vector< MFloat > &coordinates)
Loads point coordinates from an input file.
constexpr Real POW2(const Real x)
MBool approx(const T &, const U &, const T)
std::basic_string< char > MString
int MPI_Scatterv(const void *sendbuf, const int sendcount[], const int displs[], MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Scatterv
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm, const MString &name, const MString &varname)
same as MPI_Comm_create, but updates the number of MPI communicators
int MPI_Group_incl(MPI_Group group, int n, const int ranks[], MPI_Group *newgroup, const MString &name)
same as MPI_Group_incl
int MPI_Comm_group(MPI_Comm comm, MPI_Group *group, const MString &name, const MString &varname)
same as MPI_Comm_group
int MPI_Reduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Reduce
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allreduce
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Bcast
int MPI_Allgather(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allgather
int MPI_Group_free(MPI_Group *group, const MString &name)
same as MPI_Group_free
T cos(const T a, const T b, const T x)
Cosine slope filter.
Namespace for auxiliary functions/classes.
PARALLELIO_DEFAULT_BACKEND ParallelIo
MFloat dist(const Point< DIM > &p, const Point< DIM > &q)
Hold data for symmetry boundary condition.
Parameter for analytical acoustic source terms.