|
| 1 | +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +#include "currentdipoleoperator.hpp" |
| 5 | + |
| 6 | +#include "fem/coefficient.hpp" |
| 7 | +#include "utils/communication.hpp" |
| 8 | +#include "utils/geodata.hpp" |
| 9 | +#include "utils/iodata.hpp" |
| 10 | + |
| 11 | +namespace palace |
| 12 | +{ |
| 13 | + |
| 14 | +CurrentDipoleData::CurrentDipoleData(const config::CurrentDipoleData &data, |
| 15 | + const mfem::ParMesh &mesh) |
| 16 | +{ |
| 17 | + // Set up dipole moment vector |
| 18 | + moment.SetSize(mesh.SpaceDimension()); |
| 19 | + MFEM_VERIFY(data.moment.size() == static_cast<std::size_t>(mesh.SpaceDimension()), |
| 20 | + "Current dipole moment dimension must match mesh space dimension!"); |
| 21 | + for (int d = 0; d < mesh.SpaceDimension(); d++) |
| 22 | + { |
| 23 | + moment[d] = data.moment[d]; |
| 24 | + } |
| 25 | + |
| 26 | + // Set up dipole center position |
| 27 | + center.SetSize(mesh.SpaceDimension()); |
| 28 | + MFEM_VERIFY(data.center.size() == static_cast<std::size_t>(mesh.SpaceDimension()), |
| 29 | + "Current dipole center dimension must match mesh space dimension!"); |
| 30 | + for (int d = 0; d < mesh.SpaceDimension(); d++) |
| 31 | + { |
| 32 | + center[d] = data.center[d]; |
| 33 | + } |
| 34 | + |
| 35 | + // Create the VectorDeltaCoefficient |
| 36 | + dipole_coeff = std::make_unique<mfem::VectorDeltaCoefficient>(moment); |
| 37 | + dipole_coeff->SetDeltaCenter(center); |
| 38 | +} |
| 39 | + |
| 40 | +mfem::VectorDeltaCoefficient *CurrentDipoleData::GetDeltaCoefficient(double scale) const |
| 41 | +{ |
| 42 | + // Scale the dipole coefficient by the given factor |
| 43 | + dipole_coeff->SetScale(scale); |
| 44 | + return dipole_coeff.get(); |
| 45 | +} |
| 46 | + |
| 47 | +CurrentDipoleOperator::CurrentDipoleOperator(const IoData &iodata, |
| 48 | + const mfem::ParMesh &mesh) |
| 49 | +{ |
| 50 | + // Set up current dipole source properties. |
| 51 | + SetUpDipoleProperties(iodata, mesh); |
| 52 | + PrintDipoleInfo(iodata, mesh); |
| 53 | +} |
| 54 | + |
| 55 | +void CurrentDipoleOperator::SetUpDipoleProperties(const IoData &iodata, |
| 56 | + const mfem::ParMesh &mesh) |
| 57 | +{ |
| 58 | + // Set up current dipole data structures. |
| 59 | + for (const auto &[idx, data] : iodata.domains.current_dipole) |
| 60 | + { |
| 61 | + dipoles.emplace(idx, CurrentDipoleData(data, mesh)); |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +void CurrentDipoleOperator::PrintDipoleInfo(const IoData &iodata, const mfem::ParMesh &mesh) |
| 66 | +{ |
| 67 | + if (dipoles.empty()) |
| 68 | + { |
| 69 | + return; |
| 70 | + } |
| 71 | + |
| 72 | + Mpi::Print("\nConfiguring electrical current dipole source terms:\n"); |
| 73 | + for (const auto &[idx, data] : dipoles) |
| 74 | + { |
| 75 | + // Convert center coordinates back to physical units for display |
| 76 | + mfem::Vector physical_center = data.center; |
| 77 | + iodata.units.DimensionalizeInPlace<Units::ValueType::LENGTH>(physical_center); |
| 78 | + Mpi::Print(" Dipole {:d}: \n" |
| 79 | + " \tCenter = ({:.3e}, {:.3e}, {:.3e})\n" |
| 80 | + " \tMoment = ({:.3e}, {:.3e}, {:.3e})\n", |
| 81 | + idx, physical_center[0], physical_center[1], |
| 82 | + (mesh.SpaceDimension() > 2) ? physical_center[2] : 0.0, data.moment[0], |
| 83 | + data.moment[1], (mesh.SpaceDimension() > 2) ? data.moment[2] : 0.0); |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +const CurrentDipoleData &CurrentDipoleOperator::GetDipole(int idx) const |
| 88 | +{ |
| 89 | + return dipoles.at(idx); |
| 90 | +} |
| 91 | + |
| 92 | +std::vector<mfem::Vector> CurrentDipoleOperator::GetMoments() const |
| 93 | +{ |
| 94 | + std::vector<mfem::Vector> moments; |
| 95 | + moments.reserve(dipoles.size()); |
| 96 | + for (const auto &[idx, dipole] : dipoles) |
| 97 | + { |
| 98 | + moments.push_back(dipole.moment); |
| 99 | + } |
| 100 | + return moments; |
| 101 | +} |
| 102 | + |
| 103 | +std::vector<mfem::Vector> CurrentDipoleOperator::GetCenters() const |
| 104 | +{ |
| 105 | + std::vector<mfem::Vector> centers; |
| 106 | + centers.reserve(dipoles.size()); |
| 107 | + for (const auto &[idx, dipole] : dipoles) |
| 108 | + { |
| 109 | + centers.push_back(dipole.center); |
| 110 | + } |
| 111 | + return centers; |
| 112 | +} |
| 113 | + |
| 114 | +void CurrentDipoleOperator::AddExcitationDomainIntegrators(int idx, mfem::LinearForm &rhs) |
| 115 | +{ |
| 116 | + // Add only the specific dipole with the given index |
| 117 | + auto it = dipoles.find(idx); |
| 118 | + if (it != dipoles.end()) |
| 119 | + { |
| 120 | + const auto &dipole = it->second; |
| 121 | + |
| 122 | + // Create a VectorDeltaCoefficient for this specific dipole (RHS = -J_source) |
| 123 | + auto dipole_coeff = std::make_unique<mfem::VectorDeltaCoefficient>(dipole.moment); |
| 124 | + dipole_coeff->SetDeltaCenter(dipole.center); |
| 125 | + dipole_coeff->SetScale(-1.0); |
| 126 | + |
| 127 | + // Add as domain integrator |
| 128 | + rhs.AddDomainIntegrator(new mfem::VectorFEDomainLFIntegrator(*dipole_coeff)); |
| 129 | + |
| 130 | + // Store the coefficient to prevent deletion. |
| 131 | + dipole_integrator_coeffs.push_back(std::move(dipole_coeff)); |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +void CurrentDipoleOperator::AddExcitationDomainIntegrators(mfem::LinearForm &rhs) |
| 136 | +{ |
| 137 | + // Add each dipole as a separate integrator |
| 138 | + for (const auto &[idx, dipole] : dipoles) |
| 139 | + { |
| 140 | + AddExcitationDomainIntegrators(idx, rhs); |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +} // namespace palace |
0 commit comments