From fbfb1f15c57ef7d037db2bf6eb11bb7a1911a780 Mon Sep 17 00:00:00 2001 From: Maximilian Schnabel <7090722+MaximilianSchnabel@users.noreply.github.com> Date: Thu, 7 Jul 2022 14:15:43 +0200 Subject: [PATCH] GeometricalAcoustics/PathSim: implement CCombinedWorker to combine calculation of direct and reflective path --- .../EigenraySearch/EigenrayEngine.h | 8 +- .../EigenraySearch/EigenrayEngine.cpp | 51 +++- .../EigenraySearch/Worker.cpp | 239 +++++++++++++++++- .../EigenraySearch/Worker.h | 32 +++ 4 files changed, 323 insertions(+), 7 deletions(-) diff --git a/include/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.h b/include/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.h index d2e4e95..63f6bef 100644 --- a/include/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.h +++ b/include/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.h @@ -51,7 +51,13 @@ namespace ITAPropagationPathSim inline CEngine( ) { }; std::vector<std::shared_ptr<CRay>> Run( const ITAGeo::CStratifiedAtmosphere& atmosphere, const VistaVector3D& sourcePosition, - const VistaVector3D& receiverPosition ) const; + const VistaVector3D& receiverPosition, bool combineReflCalc = false) const; + + std::vector<std::shared_ptr<CRay>> Run( const ITAGeo::CStratifiedAtmosphere& atmosphere, const VistaVector3D& sourcePosition, + const VistaVector3D& receiverPosition, const VistaVector3D& initDirectDirection, + const VistaVector3D& initReflectionDirection, + double phiResolution, double thetaResolution, + bool combineReflCalc ) const; }; } // namespace EigenraySearch } // namespace AtmosphericRayTracing diff --git a/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.cpp b/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.cpp index 10f8444..4a00a78 100644 --- a/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.cpp +++ b/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/EigenrayEngine.cpp @@ -15,7 +15,7 @@ using namespace ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch; std::vector<std::shared_ptr<CRay>> CEngine::Run( const ITAGeo::CStratifiedAtmosphere& atmosphere, const VistaVector3D& sourcePosition, - const VistaVector3D& receiverPosition ) const + const VistaVector3D& receiverPosition, bool combineReflCalc ) const { Simulation::Settings initialSettings = simulationSettings; initialSettings.dIntegrationTimeStep *= 10; @@ -23,10 +23,51 @@ std::vector<std::shared_ptr<CRay>> CEngine::Run( const ITAGeo::CStratifiedAtmosp std::vector<CRayGrid> initialRayGrids = initialRayTracing.Run( atmosphere ); RayVector eigenrays; - for( int reflectionOrder = 0; reflectionOrder < initialRayGrids.size( ); reflectionOrder++ ) + if( combineReflCalc ) { - CAdaptiveWorker worker( initialRayGrids[reflectionOrder], receiverPosition, simulationSettings, eigenraySettings, reflectionOrder ); - eigenrays.push_back( worker.Run( atmosphere ) ); + CCombinedAdaptiveWorker worker( initialRayGrids[0], receiverPosition, simulationSettings, eigenraySettings, 2 ); + eigenrays = worker.Run( atmosphere ); + } + else + { + for( int reflectionOrder = 0; reflectionOrder < initialRayGrids.size( ); reflectionOrder++ ) + { + CAdaptiveWorker worker( initialRayGrids[reflectionOrder], receiverPosition, simulationSettings, eigenraySettings, reflectionOrder ); + eigenrays.push_back( worker.Run( atmosphere ) ); + } + } + return eigenrays; +} + +std::vector<std::shared_ptr<CRay>> ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CEngine::Run( const ITAGeo::CStratifiedAtmosphere& atmosphere, + const VistaVector3D& sourcePosition, + const VistaVector3D& receiverPosition, + const VistaVector3D& initDirectDirection, + const VistaVector3D& initReflectionDirection, + double phiResolution, + double thetaResolution, + bool combineReflCalc) const +{ + Simulation::Settings initialSettings = simulationSettings; + initialSettings.dIntegrationTimeStep *= 10; + CRayGrid initDirGrid = CPreinitializedRayGrid( sourcePosition, initDirectDirection, phiResolution, thetaResolution ); + + RayVector eigenrays; + if( combineReflCalc ) + { + CCombinedAdaptiveWorker worker( initDirGrid, receiverPosition, simulationSettings, eigenraySettings, 2 ); + eigenrays = worker.Run( atmosphere ); + } + else + { + CRayGrid initReflGrid = CPreinitializedRayGrid( sourcePosition, initReflectionDirection, phiResolution, thetaResolution ); + std::vector<CRayGrid> initialRayGrids = { initDirGrid, initReflGrid }; + + for( int reflectionOrder = 0; reflectionOrder < initialRayGrids.size( ); reflectionOrder++ ) + { + CAdaptiveWorker worker( initialRayGrids[reflectionOrder], receiverPosition, simulationSettings, eigenraySettings, reflectionOrder ); + eigenrays.push_back( worker.Run( atmosphere ) ); + } } return eigenrays; -} \ No newline at end of file +} diff --git a/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.cpp b/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.cpp index 5091a86..7d414cc 100644 --- a/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.cpp +++ b/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.cpp @@ -299,4 +299,241 @@ double EigenraySearch::CAdaptiveWorker::CalculateEnergyProportion( const double& const double dDenominator = A * ( 1 + dRelVToC ) * std::sqrt( 1 + 2 * dRelVToC + vSquared / ( c * c ) ); return c / dDenominator; } -#pragma endregion \ No newline at end of file +#pragma endregion + +ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::CCombinedAdaptiveWorker( const CRayGrid& rayGrid, + const VistaVector3D& receiverPosition, + const Simulation::Settings& simSettings, + const Settings& eigenraySettings, + const int reflectionOrders ) + : CWorkerBase( rayGrid.SourcePosition( ), receiverPosition, simSettings, eigenraySettings.rayTracing ) + , m_reflectionOrders( reflectionOrders ) + , m_rayAdaptationSettings( eigenraySettings.rayAdaptation ) +{ + m_adaptiveRayGrids[0] = std::make_unique<CAdaptiveRayGrid>( rayGrid ); + m_adaptations[0] = 0; + + const float sourceReceiverDistance = ( VirtualReceiverPosition(0) - rayGrid.SourcePosition( ) ).GetLength( ); + m_fReceiverRadius = (float)tan( m_rayAdaptationSettings.accuracy.maxSourceReceiverAngle * M_PI / 180.0 ) * sourceReceiverDistance; + m_fReceiverRadius = fmin( (float)m_rayAdaptationSettings.accuracy.maxReceiverRadius, m_fReceiverRadius ); + + ReceiverDataVector receiverData; + for( int reflOrder = 0; reflOrder < reflectionOrders ; ++reflOrder) + receiverData.push_back(CReceiverData( VirtualReceiverPosition( reflOrder ), reflOrder )); + + m_pSimulationWatcher = + std::make_shared<CEigenraySearchWatcher>( receiverData, m_rayTracingAbortSettings.maxTime, m_rayTracingAbortSettings.bAbortOnReceiverDistIncrease ); + m_simulationEngine.pExternalWatcher = m_pSimulationWatcher; +} + +std::vector<EigenraySearch::RayPtr> ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::Run( const ITAGeo::CStratifiedAtmosphere& atmosphere ) +{ + RunRayTracing( atmosphere, m_adaptiveRayGrids[0]->UniqueRays( ) ); + m_pMinDistanceRays = FindMinimumDistanceRays( m_reflectionOrders ); + bool keepWorking = true; + while( keepWorking ) + { + SplitIfNecessary( ); + keepWorking = false; + for( auto& [reflectionOrder, pGrid]: m_adaptiveRayGrids ) + { + RayPtr& ray = m_pMinDistanceRays[reflectionOrder]; + bool iterationWorks = !EigenrayAccuracyReached( ray, reflectionOrder ) && !AbortCriterionReached( reflectionOrder ); + keepWorking |= iterationWorks; + + if( !iterationWorks ) + continue; + + if( m_rayAdaptationSettings.advancedRayZooming.bActive ) + pGrid->ZoomIntoRay( + m_pMinDistanceRays[reflectionOrder], + VirtualReceiverPosition( reflectionOrder ), + m_rayAdaptationSettings.advancedRayZooming.threshold + ); + else + pGrid->ZoomIntoRay( m_pMinDistanceRays[reflectionOrder] ); + RunRayTracing( atmosphere, pGrid->NewRaysOfLastAdaptation( ) ); + m_adaptations[reflectionOrder]++; + } + + m_pMinDistanceRays = FindMinimumDistanceRays( m_reflectionOrders ); + } + + PostProcessEigenray( atmosphere ); + return m_pMinDistanceRays; +} + +std::vector<EigenraySearch::RayPtr> ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::FindMinimumDistanceRays( const int underReflectionOrder ) const +{ + struct ClosestRayData + { + float dMin = FLT_MAX; + RayPtr pMinRay = nullptr; + }; + std::vector<RayPtr> pMinRays = { }; + std::vector<ClosestRayData> rayData(underReflectionOrder); + + for( int reflOrder = 0; reflOrder < underReflectionOrder; ++reflOrder ) + { + bool wasSplit = m_adaptiveRayGrids.count( reflOrder ) == 1; + const std::unique_ptr<CAdaptiveRayGrid>* usedGrid = nullptr; + if( wasSplit ) + usedGrid = &m_adaptiveRayGrids.at(reflOrder); + else + usedGrid = &m_adaptiveRayGrids.at(0); + + for( const RayPtr& pRay: (*usedGrid)->UniqueRays() ) + { + const VistaVector3D& receiverPos = VirtualReceiverPosition( reflOrder ); + const CRayReceiverData* distanceData = pRay->ReceiverDistanceData( receiverPos ); + if( distanceData && distanceData->distance < rayData[reflOrder].dMin ) + { + rayData[reflOrder].dMin = distanceData->distance; + rayData[reflOrder].pMinRay = pRay; + } + } + } + + for( ClosestRayData& data: rayData) + pMinRays.push_back( data.pMinRay ); + + return pMinRays; +} + +void ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::SplitIfNecessary( ) +{ + for( int reflOrder = 1; reflOrder < m_reflectionOrders; ++reflOrder ) + { + //TODO:: check if actual ray comparison is needed instead of pointer obj comparison + if( m_pMinDistanceRays[0] != m_pMinDistanceRays[reflOrder] ) + { + if( m_adaptiveRayGrids.count( reflOrder ) != 1 ) + { + m_adaptations[reflOrder] = 0; + m_adaptiveRayGrids[reflOrder] = std::make_unique<CAdaptiveRayGrid>( *m_adaptiveRayGrids[0] ); + } + } + } +} +bool ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::EigenrayAccuracyReached( RayPtr& ray, int reflOrder ) +{ + const CRayReceiverData* receiverData = ray->ReceiverDistanceData( VirtualReceiverPosition( reflOrder ) ); + return ( receiverData && receiverData->distance <= m_fReceiverRadius ); +} + +bool ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::AbortCriterionReached( int reflOrder ) +{ + return m_adaptations[reflOrder] > m_rayAdaptationSettings.abort.maxNAdaptations || + m_adaptiveRayGrids[reflOrder]->MaxAngularResolution( ) < m_rayAdaptationSettings.abort.minAngleResolutionDeg; +} + +void ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::PostProcessEigenray( const ITAGeo::CStratifiedAtmosphere& atmosphere ) +{ + SetEigenrayStatus( ); + for( auto& [reflOrder, pGrid]: m_adaptiveRayGrids ) + { + SetEigenrayEndPoint( m_pMinDistanceRays[reflOrder], reflOrder ); + CalculateEigenraySpreadingLoss( atmosphere, m_pMinDistanceRays[reflOrder], reflOrder ); + } +} + + +void ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::SetEigenrayStatus( ) +{ + for( int reflOrder = 0; reflOrder < m_pMinDistanceRays.size(); ++reflOrder ) + { + RayPtr& ray = m_pMinDistanceRays[reflOrder]; + bool bAccuracyReached = EigenrayAccuracyReached( ray, reflOrder ); + if( !bAccuracyReached ) + std::cout << "WARNING: EigenraySearch::Engine: Could not find eigenray with proper accuracy. Abort criterion reached." << std::endl; + + ray->MarkAsEigenray( bAccuracyReached ); + } +} + +void ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::SetEigenrayEndPoint( RayPtr& ray, int reflOrder ) +{ + const CRayReceiverData* receiverData = ray->ReceiverDistanceData( VirtualReceiverPosition(reflOrder) ); + const int idxBeforeMin = receiverData->idxMinDist; + const VistaVector3D& rMin = receiverData->posMinDist; + + if( ray->NumPoints( ) < idxBeforeMin ) + ITA_EXCEPT_INVALID_PARAMETER( "Index for minimum distance out of bounds!" ); + + if( ray->NumPoints( ) == idxBeforeMin + 1 ) // Min dist point is endpoint + { + std::cout << "WARNING: EigenraySearch::Engine: Ray end point is closest point to receiver. This usually happens if maximum propagation time is too low." + << std::endl; + return; + } + + // Interpolate to new point of minimum + const VistaVector3D& r1 = ray->at( idxBeforeMin ).position; + const VistaVector3D& r2 = ray->at( idxBeforeMin + 1 ).position; + const float pathFraction = ( rMin - r1 ).GetLength( ) / ( r2 - r1 ).GetLength( ); + CRayElement newRayElement = ray->at( idxBeforeMin ).Interpolate( ray->at( idxBeforeMin + 1 ), pathFraction ); + + // Remove all points before and add new point + ray->resize( idxBeforeMin + 2 ); + ray->back( ) = newRayElement; +} + +void ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::CalculateEigenraySpreadingLoss( + const ITAGeo::CStratifiedAtmosphere& atmosphere, RayPtr& ray, int reflOrder ) +{ + const double zRef = std::abs( ray->SourcePoint( )[Vista::Z] ); + const VistaVector3D& nRef = ray->InitialDirection( ); + + const double& time = ray->back( ).timeStamp; + const double zReceiver = std::abs( ray->back( ).position[Vista::Z] ); + const VistaVector3D& nReceiver = ray->back( ).wavefrontNormal; + + const double dTheta = 180.0 / M_PI * std::acos( nRef[Vista::Z] ); + const bool bCloseToPole = dTheta < 1.0 || dTheta > 179.0; + if( bCloseToPole || m_adaptiveRayGrids.at(reflOrder)->MaxDeltaTheta( ) > m_rayAdaptationSettings.accuracy.maxAngleForGeomSpreading || !m_adaptiveRayGrids.at(reflOrder)->Is2D( ) ) + { + double dDeltaPhi = m_rayAdaptationSettings.accuracy.maxAngleForGeomSpreading; + + // Lower phi resolution if close pole to avoid numerical cancellation + const bool bIsPole = std::abs( nRef[Vista::X] ) < FLT_EPSILON && std::abs( nRef[Vista::Y] ) < FLT_EPSILON; + if( bIsPole ) + dDeltaPhi = 5.0; + else if( bCloseToPole ) + dDeltaPhi *= 10; + + m_adaptiveRayGrids[reflOrder]->ZoomIntoRay( ray, m_rayAdaptationSettings.accuracy.maxAngleForGeomSpreading, dDeltaPhi ); + auto tmpSimulationEngine = Simulation::CEngine( std::make_shared<Simulation::CAbortAtMaxTime>( time ) ); + tmpSimulationEngine.settings = m_simulationEngine.settings; + tmpSimulationEngine.Run( atmosphere, m_adaptiveRayGrids[reflOrder]->NewRaysOfLastAdaptation( ) ); + } + else + m_adaptiveRayGrids[reflOrder]->SetToSurroundingGrid( ray ); + + const double surfaceReceiver = m_adaptiveRayGrids[reflOrder]->WavefrontSurface( time ); + const double cReceiver = atmosphere.SpeedOfSound( zReceiver ); + const VistaVector3D vReceiver = atmosphere.WindVector( zReceiver ); + const double dEnergyProportionReceiver = CalculateEnergyProportion( surfaceReceiver, cReceiver, nReceiver, vReceiver ); + + if( std::isinf( dEnergyProportionReceiver ) ) + { + std::cout << "WARNING: EigenraySearch::Engine: Numerical cancellation during spreading loss calculation. Spreading loss is unset (= -1)." << std::endl; + return; + } + + const double surfaceRef = m_adaptiveRayGrids[reflOrder]->WavefrontSurfaceReference( ); + const double cRef = atmosphere.SpeedOfSound( zRef ); + const VistaVector3D vRef = atmosphere.WindVector( zRef ); + const double dEnergyProportionRef = CalculateEnergyProportion( surfaceRef, cRef, nRef, vRef ); + + ray->SetSpreadingLoss( std::sqrt( dEnergyProportionReceiver / dEnergyProportionRef ) ); +} + +double ITAPropagationPathSim::AtmosphericRayTracing::EigenraySearch::CCombinedAdaptiveWorker::CalculateEnergyProportion( const double& A, const double& c, + const VistaVector3D& vecN, + const VistaVector3D& vecV ) +{ + const double dRelVToC = (double)( vecN * vecV ) / c; + const double vSquared = vecV.GetLengthSquared( ); + const double dDenominator = A * ( 1 + dRelVToC ) * std::sqrt( 1 + 2 * dRelVToC + vSquared / ( c * c ) ); + return c / dDenominator; +} diff --git a/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.h b/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.h index 80ef570..f8d5bd3 100644 --- a/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.h +++ b/src/ITAPropagationPathSim/AtmosphericRayTracing/EigenraySearch/Worker.h @@ -158,6 +158,38 @@ namespace ITAPropagationPathSim void CalculateEigenraySpreadingLoss( const ITAGeo::CStratifiedAtmosphere& atmosphere ); double CalculateEnergyProportion( const double& A, const double& c, const VistaVector3D& vecN, const VistaVector3D& vecV ); }; + + class CCombinedAdaptiveWorker : public CWorkerBase + { + private: + const int m_reflectionOrders; + RayAdaptationSettings m_rayAdaptationSettings; + float m_fReceiverRadius; + + std::vector<RayPtr> m_pMinDistanceRays; + //int m_nAdaptations = 0; + + std::map<int, std::unique_ptr<CAdaptiveRayGrid>> m_adaptiveRayGrids; + std::map<int, int> m_adaptations; + + public: + CCombinedAdaptiveWorker( const CRayGrid& rayGrid, const VistaVector3D& receiverPosition, const Simulation::Settings& simSettings, + const Settings& eigenraySettings, const int reflectionOrders ); + + std::vector<RayPtr> Run( const ITAGeo::CStratifiedAtmosphere& atmosphere ); + + private: + std::vector<RayPtr> FindMinimumDistanceRays( const int underReflectionOrder ) const; + + void SplitIfNecessary( ); + bool EigenrayAccuracyReached( RayPtr& ray, int reflOrder ); + bool AbortCriterionReached( int reflOrder ); + void PostProcessEigenray( const ITAGeo::CStratifiedAtmosphere& atmosphere ); + void SetEigenrayStatus( ); + void SetEigenrayEndPoint( RayPtr& ray, int reflOrder ); + void CalculateEigenraySpreadingLoss( const ITAGeo::CStratifiedAtmosphere& atmosphere, RayPtr& ray, int reflOrder ); + double CalculateEnergyProportion( const double& A, const double& c, const VistaVector3D& vecN, const VistaVector3D& vecV ); + }; } // namespace EigenraySearch } // namespace AtmosphericRayTracing } // namespace ITAPropagationPathSim -- GitLab