@@ -29,8 +29,31 @@ SGBCHelperFields initSGBCHelperFields(const int size)
2929 return res;
3030}
3131
32+ static mfem::Array<int > buildSurfaceMarkerFromTags (const std::vector<int >& tags, const mfem::ParFiniteElementSpace& fes)
33+ {
34+ mfem::Array<int > marker (fes.GetMesh ()->bdr_attributes .Max ());
35+ marker = 0 ;
36+ for (const int t : tags) {
37+ marker[t - 1 ] = 1 ;
38+ }
39+ return marker;
40+ }
41+
42+ static std::vector<int > collectRCSSurfaceTags (const Probes& probes)
43+ {
44+ std::unordered_set<int > unique_tags;
45+ for (const auto & p : probes.rcsSurfaceProbes ) {
46+ for (const int t : p.tags ) {
47+ unique_tags.insert (t);
48+ }
49+ }
50+ std::vector<int > tags (unique_tags.begin (), unique_tags.end ());
51+ std::sort (tags.begin (), tags.end ());
52+ return tags;
53+ }
54+
3255GlobalEvolution::GlobalEvolution (
33- mfem::ParFiniteElementSpace& fes, Model& model, SourcesManager& srcmngr, EvolutionOptions& options) :
56+ mfem::ParFiniteElementSpace& fes, Model& model, SourcesManager& srcmngr, EvolutionOptions& options, const Probes& probes ) :
3457 mfem::TimeDependentOperator (numberOfFieldComponents* numberOfMaxDimensions* fes.GetNDofs()),
3558 fes_{ fes },
3659 model_{ model },
@@ -312,7 +335,7 @@ GlobalEvolution::GlobalEvolution(
312335 numberOfFieldComponents * numberOfMaxDimensions * (fes_.GetNDofs () + fes_.num_face_nbr_dofs )
313336 );
314337
315- Probes probes ;
338+ Probes emptyProbes ;
316339
317340 // Keep TFSF submesh infrastructure for planewave source evaluation
318341 if (model_.getTotalFieldScatteredFieldToMarker ().find (BdrCond::TotalFieldIn) != model_.getTotalFieldScatteredFieldToMarker ().end ()) {
@@ -323,7 +346,7 @@ GlobalEvolution::GlobalEvolution(
323346 }
324347
325348 // Build all operators on the global mesh
326- ProblemDescription pd (model_, probes , srcmngr_.sources , opts_);
349+ ProblemDescription pd (model_, emptyProbes , srcmngr_.sources , opts_);
327350 DGOperatorFactory<mfem::ParFiniteElementSpace> dgops (pd, fes_);
328351 globalOperator_ = dgops.buildGlobalOperator ();
329352
@@ -371,6 +394,61 @@ GlobalEvolution::GlobalEvolution(
371394 }
372395 }
373396 }
397+
398+ const bool has_rcs_probe = !probes.rcsSurfaceProbes .empty ();
399+ const bool has_mor_probe = !probes.morStateProbes .empty ();
400+ if (opts_.export_evolution_operator && has_rcs_probe && has_mor_probe) {
401+ if (Mpi::WorldSize () == 1 ) {
402+ const auto rcs_tags = collectRCSSurfaceTags (probes);
403+ if (!rcs_tags.empty ()) {
404+ auto marker = buildSurfaceMarkerFromTags (rcs_tags, fes_);
405+ NearToFarFieldSubMesher ntff_submesher (model_.getConstMesh (), fes_, marker);
406+
407+ auto * ntff_submesh = ntff_submesher.getSubMesh ();
408+ auto dgfec = dynamic_cast <const mfem::DG_FECollection*>(fes_.FEColl ());
409+ if (!dgfec) {
410+ throw std::runtime_error (" The FiniteElementCollection in the FiniteElementSpace is not DG." );
411+ }
412+
413+ mfem::FiniteElementSpace ntff_fes (ntff_submesh, dgfec);
414+ mfem::Array<int > farfield_sub_to_parent_ids;
415+ mfem::SubMeshUtils::BuildVdofToVdofMap (ntff_fes,
416+ fes_,
417+ ntff_submesh->GetFrom (),
418+ ntff_submesh->GetParentElementIDMap (),
419+ farfield_sub_to_parent_ids);
420+
421+ const int farfield_ndofs = farfield_sub_to_parent_ids.Size ();
422+ const int parent_ndofs = fes_.GetNDofs ();
423+ auto farfield_mapping_matrix = std::make_unique<mfem::SparseMatrix>(6 * farfield_ndofs, 6 * parent_ndofs);
424+
425+ for (int comp = 0 ; comp < 6 ; ++comp) {
426+ for (int sub_dof = 0 ; sub_dof < farfield_ndofs; ++sub_dof) {
427+ const int parent_dof = farfield_sub_to_parent_ids[sub_dof];
428+ const int row = comp * farfield_ndofs + sub_dof;
429+ const int col = comp * parent_ndofs + parent_dof;
430+ farfield_mapping_matrix->Set (row, col, 1.0 );
431+ }
432+ }
433+ farfield_mapping_matrix->Finalize ();
434+
435+ std::filesystem::path export_dir = std::filesystem::path (" Exports" ) / " Operators" / model_.meshName_ ;
436+ if (!std::filesystem::exists (export_dir)) {
437+ std::filesystem::create_directories (export_dir);
438+ }
439+
440+ std::filesystem::path farfield_path = export_dir / (model_.meshName_ + " _farfield.csr" );
441+ std::ofstream ofs_farfield (farfield_path);
442+ if (!ofs_farfield.is_open ()) {
443+ throw std::runtime_error (" Could not open file for writing: " + farfield_path.string ());
444+ }
445+ farfield_mapping_matrix->PrintCSR2 (ofs_farfield);
446+ ofs_farfield.close ();
447+ std::cout << " Farfield mapping matrix exported to " << farfield_path << std::endl;
448+ }
449+ }
450+ }
451+
374452 if (model_.getSGBCToMarker ().find (BdrCond::SGBC) != model_.getSGBCToMarker ().end ()) {
375453 SGBCOperator_ = dgops.buildSGBCGlobalOperator ();
376454 }
0 commit comments