19 const double z,
double& ex,
double& ey,
20 double& ez,
double& p,
Medium*& m,
28 <<
" Field map is not available for interpolation.\n";
35 if (!GetField(x, y, z, m_efields, ex, ey, ez, p, region)) {
40 if (region < 0 || region > (
int)m_media.size()) {
50 const double z,
double& ex,
double& ey,
51 double& ez,
Medium*& m,
int& status) {
57 const double z,
double& wx,
double& wy,
58 double& wz,
const std::string& ) {
61 if (!m_hasWfield)
return;
62 const double xx = x - m_wField_xOffset;
63 const double yy = y - m_wField_yOffset;
64 const double zz = z - m_wField_zOffset;
67 GetField(xx, yy, zz, m_wfields, wx, wy, wz, wp, region);
72 const std::string& ) {
73 if (!m_hasWfield)
return 0.;
74 const double xx = x - m_wField_xOffset;
75 const double yy = y - m_wField_yOffset;
76 const double zz = z - m_wField_zOffset;
77 double wx = 0., wy = 0., wz = 0.;
80 if (!GetField(xx, yy, zz, m_wfields, wx, wy, wz, wp, region))
return 0.;
85 const double z,
const double t,
86 double& wx,
double& wy,
double& wz,
87 const std::string& ) {
90 if (m_wdtimes.empty())
return;
92 if (t < m_wdtimes.front() || t > m_wdtimes.back())
return;
94 const double xx = x - m_wField_xOffset;
95 const double yy = y - m_wField_yOffset;
96 const double zz = z - m_wField_zOffset;
98 const auto it1 = std::upper_bound(m_wdtimes.cbegin(), m_wdtimes.cend(), t);
99 const auto it0 = std::prev(it1);
101 const double dt = t - *it0;
104 const unsigned int i0 = it0 - m_wdtimes.cbegin();
105 double wx0 = 0., wy0 = 0., wz0 = 0.;
106 if (!GetField(xx, yy, zz, m_wdfields[i0], wx0, wy0, wz0, wp, region)) {
109 if (dt < Small || it1 == m_wdtimes.cend()) {
115 const unsigned int i1 = it1 - m_wdtimes.cbegin();
116 double wx1 = 0., wy1 = 0., wz1 = 0.;
117 if (!GetField(xx, yy, zz, m_wdfields[i1], wx1, wy1, wz1, wp, region)) {
120 const double f1 = dt / (*it1 - *it0);
121 const double f0 = 1. - f1;
122 wx = f0 * wx0 + f1 * wx1;
123 wy = f0 * wy0 + f1 * wy1;
124 wz = f0 * wz0 + f1 * wz1;
129 m_wField_xOffset = x;
130 m_wField_yOffset = y;
131 m_wField_zOffset = z;
135 const double z,
double& bx,
double& by,
136 double& bz,
int& status) {
144 if (!GetField(x, y, z, m_bfields, bx, by, bz, p, region)) {
154 <<
" Field map is not available for interpolation.\n";
158 unsigned int i, j, k;
159 bool xMirrored, yMirrored, zMirrored;
160 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
163 const int region = m_regions[i][j][k];
164 if (region < 0 || region > (
int)m_media.size())
return nullptr;
165 return m_media[region];
169 const unsigned int nz,
const double xmin,
170 const double xmax,
const double ymin,
171 const double ymax,
const double zmin,
174 if (nx == 0 || ny == 0 || nz == 0) {
176 <<
" Number of mesh elements must be positive.\n";
180 std::cerr <<
m_className <<
"::SetMesh: Invalid x range.\n";
182 }
else if (ymin >= ymax) {
183 std::cerr <<
m_className <<
"::SetMesh: Invalid y range.\n";
185 }
else if (zmin >= zmax) {
186 std::cerr <<
m_className <<
"::SetMesh: Invalid z range.\n";
198 m_dx = (m_xMax - m_xMin) / m_nX;
199 m_dy = (m_yMax - m_yMin) / m_nY;
200 m_dz = (m_zMax - m_zMin) / m_nZ;
205 const std::string& fmt,
206 const bool withP,
const bool withR,
207 const double scaleX,
const double scaleE,
208 const double scaleP) {
211 m_hasPotential = m_hasEfield =
false;
213 std::cerr <<
m_className <<
"::LoadElectricField:\n"
214 <<
" Mesh is not set. Call SetMesh first.\n";
219 Initialise(m_efields);
222 m_pMin = m_pMax = 0.;
227 if (!LoadData(fname, fmt, withP, withR, scaleX, scaleE, scaleP, m_efields)) {
232 if (withP) m_hasPotential =
true;
237 const std::string& fmt,
241 const double scaleP) {
244 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
245 <<
" Mesh is not set. Call SetMesh first.\n";
250 Initialise(m_wfields);
251 if (m_regions.empty()) InitialiseRegions();
254 if (!LoadData(fname, fmt, withP,
false, scaleX, scaleE, scaleP, m_wfields)) {
261 const std::string& fmt,
262 const double t,
const bool withP,
263 const double scaleX,
const double scaleE,
264 const double scaleP) {
267 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
268 <<
" Mesh is not set. Call SetMesh first.\n";
272 std::vector<std::vector<std::vector<Element> > > wfield;
274 if (m_regions.empty()) InitialiseRegions();
277 if (!LoadData(fname, fmt, withP,
false, scaleX, scaleE, scaleP, wfield)) {
280 if (m_wdtimes.empty() || t > m_wdtimes.back()) {
281 m_wdtimes.push_back(t);
282 m_wdfields.push_back(std::move(wfield));
284 const auto it = std::upper_bound(m_wdtimes.cbegin(), m_wdtimes.cend(), t);
285 const auto n = std::distance(m_wdtimes.cbegin(), it);
286 m_wdtimes.insert(it, t);
287 m_wdfields.insert(m_wdfields.cbegin() + n, std::move(wfield));
293 const std::string& fmt,
295 const double scaleB) {
298 std::cerr <<
m_className <<
"::LoadMagneticField:\n"
299 <<
" Mesh is not set. Call SetMesh first.\n";
304 Initialise(m_bfields);
308 if (!LoadData(fname, fmt,
false,
false, scaleX, scaleB, 1., m_bfields)) {
315bool ComponentVoxel::LoadData(
const std::string& filename, std::string format,
316 const bool withPotential,
const bool withRegion,
317 const double scaleX,
const double scaleF,
const double scaleP,
318 std::vector<std::vector<std::vector<Element> > >& fields) {
321 std::cerr <<
m_className <<
"::LoadData: Mesh has not been set.\n";
325 unsigned int nValues = 0;
327 std::vector<std::vector<std::vector<bool> > > isSet(
329 std::vector<std::vector<bool> >(m_nY, std::vector<bool>(m_nZ,
false)));
331 std::ifstream infile;
332 infile.open(filename.c_str(), std::ios::in);
335 <<
" Could not open file " << filename <<
".\n";
339 std::transform(format.begin(), format.end(), format.begin(), toupper);
340 unsigned int fmt = 0;
341 if (format ==
"XY") {
343 }
else if (format ==
"XYZ") {
345 }
else if (format ==
"IJ") {
347 }
else if (format ==
"IJK") {
349 }
else if (format ==
"YXZ") {
353 <<
" Unknown format (" << format <<
").\n";
357 unsigned int nLines = 0;
359 while (!infile.fail()) {
361 std::getline(infile, line);
366 if (line.empty())
continue;
368 if (line[0] ==
'#')
continue;
369 if (line[0] ==
'/' && line[1] ==
'/')
continue;
378 std::istringstream data;
386 <<
" Error reading line " << nLines <<
".\n"
387 <<
" Cannot retrieve element coordinates.\n";
393 const double z = 0.5 * (m_zMin + m_zMax);
394 bool xMirrored, yMirrored, zMirrored;
395 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
397 <<
" Error reading line " << nLines <<
".\n"
398 <<
" Point is outside mesh.\n";
402 }
else if (fmt == 2) {
408 <<
" Error reading line " << nLines <<
".\n"
409 <<
" Cannot retrieve element coordinates.\n";
416 bool xMirrored, yMirrored, zMirrored;
417 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
419 <<
" Error reading line " << nLines <<
".\n"
420 <<
" Point is outside mesh.\n";
424 }
else if (fmt == 3) {
430 <<
" Error reading line " << nLines <<
".\n"
431 <<
" Cannot retrieve element index.\n";
435 }
else if (fmt == 4) {
440 <<
" Error reading line " << nLines <<
".\n"
441 <<
" Cannot retrieve element index.\n";
445 }
else if (fmt == 5) {
447 double x,
y,
z, temp;
448 data >>
y >>
x >> temp;
452 <<
" Error reading line " << nLines <<
".\n"
453 <<
" Cannot retrieve element coordinates.\n";
460 bool xMirrored, yMirrored, zMirrored;
461 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
463 <<
" Error reading line " << nLines <<
".\n"
464 <<
" Point is outside mesh.\n";
470 if (i >= m_nX || j >= m_nY || k >= m_nZ) {
472 <<
" Error reading line " << nLines <<
".\n"
473 <<
" Index (" << i <<
", " << j <<
", " << k
474 <<
") out of range.\n";
477 if (isSet[i][j][k]) {
479 <<
" Error reading line " << nLines <<
".\n"
480 <<
" Mesh element (" << i <<
", " << j <<
", " << k
481 <<
") has already been set.\n";
485 if (fmt == 1 || fmt == 3) {
489 }
else if (fmt == 5) {
491 data >> fy >> fx >> temp;
494 data >> fx >> fy >> fz;
498 <<
" Error reading line " << nLines <<
".\n"
499 <<
" Cannot read field values.\n";
510 <<
" Error reading line " << nLines <<
".\n"
511 <<
" Cannot read potential.\n";
516 if (m_pMin > m_pMax) {
521 if (v < m_pMin) m_pMin = v;
522 if (v > m_pMax) m_pMax = v;
529 <<
" Error reading line " << nLines <<
".\n"
530 <<
" Cannot read region.\n";
535 if (fmt == 1 || fmt == 3) {
537 for (
unsigned int kk = 0; kk < m_nZ; ++kk) {
538 fields[i][j][kk].fx = fx;
539 fields[i][j][kk].fy = fy;
540 fields[i][j][kk].fz = fz;
541 fields[i][j][kk].v = v;
542 if (withRegion) m_regions[i][j][kk] = region;
543 isSet[i][j][kk] =
true;
546 fields[i][j][k].fx = fx;
547 fields[i][j][k].fy = fy;
548 fields[i][j][k].fz = fz;
549 fields[i][j][k].v = v;
550 if (withRegion) m_regions[i][j][k] = region;
551 isSet[i][j][k] =
true;
555 if (bad)
return false;
557 <<
" Read " << nValues <<
" values from " << filename <<
".\n";
558 unsigned int nExpected = m_nX * m_nY;
559 if (fmt == 2 || fmt == 4 || fmt == 5) nExpected *= m_nZ;
560 if (nExpected != nValues) {
562 <<
" Expected " << nExpected <<
" values.\n";
568 double& xmax,
double& ymax,
double& zmax) {
604 double& eymin,
double& eymax,
605 double& ezmin,
double& ezmax) {
607 std::cerr <<
m_className <<
"::GetElectricFieldRange:\n"
608 <<
" Field map is not ready for interpolation.\n";
612 exmin = exmax = m_efields[0][0][0].fx;
613 eymin = eymax = m_efields[0][0][0].fy;
614 ezmin = ezmax = m_efields[0][0][0].fz;
615 for (
unsigned int i = 0; i < m_nX; ++i) {
616 for (
unsigned int j = 0; j < m_nY; ++j) {
617 for (
unsigned int k = 0; k < m_nZ; ++k) {
618 const Element& element = m_efields[i][j][k];
619 if (element.fx < exmin) exmin = element.fx;
620 if (element.fx > exmax) exmax = element.fx;
621 if (element.fy < eymin) eymin = element.fy;
622 if (element.fy > eymax) eymax = element.fy;
623 if (element.fz < ezmin) ezmin = element.fz;
624 if (element.fz > ezmax) ezmax = element.fz;
635 <<
" Field map not yet initialised.\n";
639 if (m_media.empty()) {
640 std::cerr <<
m_className <<
"::PrintRegions: No regions defined.\n";
645 std::cout <<
" Index Medium\n";
646 const unsigned int nMedia = m_media.size();
647 for (
unsigned int i = 0; i < nMedia; ++i) {
648 const std::string name = m_media[i] ? m_media[i]->GetName() :
"none";
649 std::cout <<
" " << i <<
" " << name <<
"\n";
655 std::cerr <<
m_className <<
"::SetMedium: Null pointer.\n";
656 if (m_media.empty())
return;
658 if (i >= m_media.size()) m_media.resize(i + 1,
nullptr);
663 if (i > m_media.size()) {
664 std::cerr <<
m_className <<
"::GetMedium: Index out of range.\n";
670bool ComponentVoxel::GetField(
671 const double xi,
const double yi,
const double zi,
672 const std::vector<std::vector<std::vector<Element> > >& field,
double& fx,
673 double& fy,
double& fz,
double& p,
int& region) {
675 std::cerr <<
m_className <<
"::GetField: Mesh is not set.\n";
681 bool xMirrored =
false;
684 if (x < m_xMin || x > m_xMax)
return false;
685 bool yMirrored =
false;
688 if (y < m_yMin || y > m_yMax)
return false;
689 bool zMirrored =
false;
692 if (z < m_zMin || z > m_zMax)
return false;
695 const double sx = (x - m_xMin) / m_dx;
696 const double sy = (y - m_yMin) / m_dy;
697 const double sz = (z - m_zMin) / m_dz;
698 unsigned int i =
static_cast<unsigned int>(sx);
699 unsigned int j =
static_cast<unsigned int>(sy);
700 unsigned int k =
static_cast<unsigned int>(sz);
701 if (i >= m_nX) i = m_nX - 1;
702 if (j >= m_nY) j = m_nY - 1;
703 if (k >= m_nZ) k = m_nZ - 1;
704 region = m_regions[i][j][k];
709 const double tx = sx - 0.5;
710 const double ty = sy - 0.5;
711 const double tz = sz - 0.5;
712 int i0 =
static_cast<int>(std::floor(tx));
713 int j0 =
static_cast<int>(std::floor(ty));
714 int k0 =
static_cast<int>(std::floor(tz));
718 unsigned int i1 = i0 + 1;
719 unsigned int j1 = j0 + 1;
720 unsigned int k1 = k0 + 1;
748 if (i1 >= m_nX) i1 = perx ? 0 : m_nX - 1;
749 if (j1 >= m_nY) j1 = pery ? 0 : m_nY - 1;
750 if (k1 >= m_nZ) k1 = perz ? 0 : m_nZ - 1;
751 const Element& n000 = field[i0][j0][k0];
752 const Element& n100 = field[i1][j0][k0];
753 const Element& n010 = field[i0][j1][k0];
754 const Element& n110 = field[i1][j1][k0];
755 const Element& n001 = field[i0][j0][k1];
756 const Element& n101 = field[i1][j0][k1];
757 const Element& n011 = field[i0][j1][k1];
758 const Element& n111 = field[i1][j1][k1];
760 const double ux = 1. - vx;
761 const double uy = 1. - vy;
762 const double uz = 1. - vz;
764 std::cout <<
m_className <<
"::GetField:\n Determining field at ("
765 << xi <<
", " << yi <<
", " << zi <<
").\n"
766 <<
" X: " << i0 <<
" (" << ux <<
") - "
767 << i1 <<
" (" << vx <<
").\n"
768 <<
" Y: " << j0 <<
" (" << uy <<
") - "
769 << j1 <<
" (" << vy <<
").\n"
770 <<
" Z: " << k0 <<
" (" << uz <<
") - "
771 << k1 <<
" (" << vz <<
").\n";
773 fx = ((n000.fx * ux + n100.fx * vx) * uy +
774 (n010.fx * ux + n110.fx * vx) * vy) *
776 ((n001.fx * ux + n101.fx * vx) * uy +
777 (n011.fx * ux + n111.fx * vx) * vy) *
779 fy = ((n000.fy * ux + n100.fy * vx) * uy +
780 (n010.fy * ux + n110.fy * vx) * vy) *
782 ((n001.fy * ux + n101.fy * vx) * uy +
783 (n011.fy * ux + n111.fy * vx) * vy) *
785 fz = ((n000.fz * ux + n100.fz * vx) * uy +
786 (n010.fz * ux + n110.fz * vx) * vy) *
788 ((n001.fz * ux + n101.fz * vx) * uy +
789 (n011.fz * ux + n111.fz * vx) * vy) *
791 p = ((n000.v * ux + n100.v * vx) * uy + (n010.v * ux + n110.v * vx) * vy) *
793 ((n001.v * ux + n101.v * vx) * uy + (n011.v * ux + n111.v * vx) * vy) *
796 const Element& element = field[i][j][k];
802 if (xMirrored) fx = -fx;
803 if (yMirrored) fy = -fy;
804 if (zMirrored) fz = -fz;
809 const double zi,
unsigned int& i,
810 unsigned int& j,
unsigned int& k,
811 bool& xMirrored,
bool& yMirrored,
812 bool& zMirrored)
const {
814 std::cerr <<
m_className <<
"::GetElement: Mesh is not set.\n";
822 if (x < m_xMin || x > m_xMax)
return false;
825 if (y < m_yMin || y > m_yMax)
return false;
828 if (z < m_zMin || z > m_zMax)
return false;
831 i = (
unsigned int)((x - m_xMin) / m_dx);
832 j = (
unsigned int)((y - m_yMin) / m_dy);
833 k = (
unsigned int)((z - m_zMin) / m_dz);
834 if (i >= m_nX) i = m_nX - 1;
835 if (j >= m_nY) j = m_nY - 1;
836 if (k >= m_nZ) k = m_nZ - 1;
841 const unsigned int k,
double& v,
double& ex,
842 double& ey,
double& ez)
const {
843 v = ex = ey = ez = 0.;
846 std::cerr <<
m_className <<
"::GetElement: Mesh not set.\n";
849 std::cerr <<
m_className <<
"::GetElement: Field map not set.\n";
852 if (i >= m_nX || j >= m_nY || k >= m_nZ) {
853 std::cerr <<
m_className <<
"::GetElement: Index out of range.\n";
856 const Element& element = m_efields[i][j][k];
864void ComponentVoxel::Reset() {
873 m_nX = m_nY = m_nZ = 0;
874 m_xMin = m_yMin = m_zMin = 0.;
875 m_xMax = m_yMax = m_zMax = 0.;
876 m_pMin = m_pMax = 0.;
880 m_hasPotential =
false;
886 m_wField_xOffset = 0.;
887 m_wField_yOffset = 0.;
888 m_wField_zOffset = 0.;
891void ComponentVoxel::UpdatePeriodicity() {
893 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
894 <<
" Field map not available.\n";
899 for (
unsigned int i = 0; i < 3; ++i) {
901 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
902 <<
" Both simple and mirror periodicity requested. Reset.\n";
908 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
909 <<
" Axial symmetry is not supported. Reset.\n";
915 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
916 <<
" Rotation symmetry is not supported. Reset.\n";
921double ComponentVoxel::Reduce(
const double xin,
const double xmin,
922 const double xmax,
const bool simplePeriodic,
923 const bool mirrorPeriodic,
bool& mirrored)
const {
926 const double lx = xmax - xmin;
927 if (simplePeriodic) {
928 x = xmin + fmod(x - xmin, lx);
929 if (x < xmin)
x += lx;
930 }
else if (mirrorPeriodic) {
931 double xNew = xmin + fmod(x - xmin, lx);
932 if (xNew < xmin) xNew += lx;
933 const int nx = int(floor(0.5 + (xNew - x) / lx));
934 if (nx != 2 * (nx / 2)) {
935 xNew = xmin + xmax - xNew;
943void ComponentVoxel::Initialise(
944 std::vector<std::vector<std::vector<Element> > >& fields) {
947 for (
unsigned int i = 0; i < m_nX; ++i) {
948 fields[i].resize(m_nY);
949 for (
unsigned int j = 0; j < m_nY; ++j) {
950 fields[i][j].resize(m_nZ);
951 for (
unsigned int k = 0; k < m_nZ; ++k) {
952 fields[i][j][k].fx = 0.;
953 fields[i][j][k].fy = 0.;
954 fields[i][j][k].fz = 0.;
955 fields[i][j][k].v = 0.;
961void ComponentVoxel::InitialiseRegions() {
962 if (!m_hasMesh)
return;
963 m_regions.resize(m_nX);
964 for (
unsigned int i = 0; i < m_nX; ++i) {
965 m_regions[i].resize(m_nY);
966 for (
unsigned int j = 0; j < m_nY; ++j) {
967 m_regions[i][j].assign(m_nZ, 0);
Abstract base class for components.
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
std::array< bool, 3 > m_axiallyPeriodic
Axial periodicity in x, y, z.
virtual void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
bool m_ready
Ready for use?
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
std::string m_className
Class name.
bool m_debug
Switch on/off debugging messages.
ComponentVoxel()
Constructor.
void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status) override
void SetWeightingFieldOffset(const double x, const double y, const double z)
void PrintRegions() const
Print all regions.
bool LoadWeightingField(const std::string &filename, const std::string &format, const bool withPotential, const double scaleX=1., const double scaleE=1., const double scaleP=1.)
Import (prompt) weighting field from file.
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
bool GetElectricFieldRange(double &exmin, double &exmax, double &eymin, double &eymax, double &ezmin, double &ezmax)
void DelayedWeightingField(const double x, const double y, const double z, const double t, double &wx, double &wy, double &wz, const std::string &label) override
bool GetElement(const double xi, const double yi, const double zi, unsigned int &i, unsigned int &j, unsigned int &k, bool &xMirrored, bool &yMirrored, bool &zMirrored) const
Return the indices of the element at a given point.
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
void SetMedium(const unsigned int i, Medium *m)
Set the medium in region i.
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&m, int &status) override
Calculate the drift field [V/cm] and potential [V] at (x, y, z).
void SetMesh(const unsigned int nx, const unsigned int ny, const unsigned int nz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax)
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
bool LoadElectricField(const std::string &filename, const std::string &format, const bool withPotential, const bool withRegion, const double scaleX=1., const double scaleE=1., const double scaleP=1.)
bool LoadMagneticField(const std::string &filename, const std::string &format, const double scaleX=1., const double scaleB=1.)
Import magnetic field values from a file.
Abstract base class for media.
void ltrim(std::string &line)