17 const double z,
double& ex,
double& ey,
18 double& ez,
double& p,
Medium*& m,
26 <<
" Field map is not available for interpolation.\n";
33 if (!GetField(x, y, z, m_efields, ex, ey, ez, p, region)) {
38 if (region < 0 || region > (
int)m_media.size()) {
48 const double z,
double& ex,
double& ey,
49 double& ez,
Medium*& m,
int& status) {
55 const double z,
double& wx,
double& wy,
56 double& wz,
const std::string& ) {
59 if (!m_hasWfield)
return;
60 const double xx = x - m_wField_xOffset;
61 const double yy = y - m_wField_yOffset;
62 const double zz = z - m_wField_zOffset;
65 GetField(xx, yy, zz, m_wfields, wx, wy, wz, wp, region);
70 const std::string& ) {
71 if (!m_hasWfield)
return 0.;
72 const double xx = x - m_wField_xOffset;
73 const double yy = y - m_wField_yOffset;
74 const double zz = z - m_wField_zOffset;
75 double wx = 0., wy = 0., wz = 0.;
78 if (!GetField(xx, yy, zz, m_wfields, wx, wy, wz, wp, region))
return 0.;
83 const double z,
const double t,
84 double& wx,
double& wy,
double& wz,
85 const std::string& ) {
88 if (m_wdtimes.empty())
return;
90 if (t < m_wdtimes.front() || t > m_wdtimes.back())
return;
92 const double xx = x - m_wField_xOffset;
93 const double yy = y - m_wField_yOffset;
94 const double zz = z - m_wField_zOffset;
96 const auto it1 = std::upper_bound(m_wdtimes.cbegin(), m_wdtimes.cend(), t);
97 const auto it0 = std::prev(it1);
99 const double dt = t - *it0;
102 const unsigned int i0 = it0 - m_wdtimes.cbegin();
103 double wx0 = 0., wy0 = 0., wz0 = 0.;
104 if (!GetField(xx, yy, zz, m_wdfields[i0], wx0, wy0, wz0, wp, region)) {
107 if (dt < Small || it1 == m_wdtimes.cend()) {
113 const unsigned int i1 = it1 - m_wdtimes.cbegin();
114 double wx1 = 0., wy1 = 0., wz1 = 0.;
115 if (!GetField(xx, yy, zz, m_wdfields[i1], wx1, wy1, wz1, wp, region)) {
118 const double f1 = dt / (*it1 - *it0);
119 const double f0 = 1. - f1;
120 wx = f0 * wx0 + f1 * wx1;
121 wy = f0 * wy0 + f1 * wy1;
122 wz = f0 * wz0 + f1 * wz1;
127 m_wField_xOffset = x;
128 m_wField_yOffset = y;
129 m_wField_zOffset = z;
133 const double z,
double& bx,
double& by,
134 double& bz,
int& status) {
142 if (!GetField(x, y, z, m_bfields, bx, by, bz, p, region)) {
152 <<
" Field map is not available for interpolation.\n";
156 unsigned int i, j, k;
157 bool xMirrored, yMirrored, zMirrored;
158 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
161 const int region = m_regions[i][j][k];
162 if (region < 0 || region > (
int)m_media.size())
return nullptr;
163 return m_media[region];
167 const unsigned int nz,
const double xmin,
168 const double xmax,
const double ymin,
169 const double ymax,
const double zmin,
172 if (nx == 0 || ny == 0 || nz == 0) {
174 <<
" Number of mesh elements must be positive.\n";
178 std::cerr <<
m_className <<
"::SetMesh: Invalid x range.\n";
180 }
else if (ymin >= ymax) {
181 std::cerr <<
m_className <<
"::SetMesh: Invalid y range.\n";
183 }
else if (zmin >= zmax) {
184 std::cerr <<
m_className <<
"::SetMesh: Invalid z range.\n";
196 m_dx = (m_xMax - m_xMin) / m_nX;
197 m_dy = (m_yMax - m_yMin) / m_nY;
198 m_dz = (m_zMax - m_zMin) / m_nZ;
203 const std::string& fmt,
204 const bool withP,
const bool withR,
205 const double scaleX,
const double scaleE,
206 const double scaleP) {
209 m_hasPotential = m_hasEfield =
false;
211 std::cerr <<
m_className <<
"::LoadElectricField:\n"
212 <<
" Mesh is not set. Call SetMesh first.\n";
217 Initialise(m_efields);
220 m_pMin = m_pMax = 0.;
225 if (!LoadData(fname, fmt, withP, withR, scaleX, scaleE, scaleP, m_efields)) {
230 if (withP) m_hasPotential =
true;
235 const std::string& fmt,
239 const double scaleP) {
242 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
243 <<
" Mesh is not set. Call SetMesh first.\n";
248 Initialise(m_wfields);
249 if (m_regions.empty()) InitialiseRegions();
252 if (!LoadData(fname, fmt, withP,
false, scaleX, scaleE, scaleP, m_wfields)) {
259 const std::string& fmt,
260 const double t,
const bool withP,
261 const double scaleX,
const double scaleE,
262 const double scaleP) {
265 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
266 <<
" Mesh is not set. Call SetMesh first.\n";
270 std::vector<std::vector<std::vector<Element> > > wfield;
272 if (m_regions.empty()) InitialiseRegions();
275 if (!LoadData(fname, fmt, withP,
false, scaleX, scaleE, scaleP, wfield)) {
278 if (m_wdtimes.empty() || t > m_wdtimes.back()) {
279 m_wdtimes.push_back(t);
280 m_wdfields.push_back(std::move(wfield));
282 const auto it = std::upper_bound(m_wdtimes.begin(), m_wdtimes.end(), t);
283 const auto n = std::distance(m_wdtimes.begin(), it);
284 m_wdtimes.insert(it, t);
285 m_wdfields.insert(m_wdfields.begin() + n, std::move(wfield));
291 const std::string& fmt,
293 const double scaleB) {
296 std::cerr <<
m_className <<
"::LoadMagneticField:\n"
297 <<
" Mesh is not set. Call SetMesh first.\n";
302 Initialise(m_bfields);
306 if (!LoadData(fname, fmt,
false,
false, scaleX, scaleB, 1., m_bfields)) {
313bool ComponentVoxel::LoadData(
const std::string& filename, std::string format,
314 const bool withPotential,
const bool withRegion,
315 const double scaleX,
const double scaleF,
const double scaleP,
316 std::vector<std::vector<std::vector<Element> > >& fields) {
319 std::cerr <<
m_className <<
"::LoadData: Mesh has not been set.\n";
323 unsigned int nValues = 0;
325 std::vector<std::vector<std::vector<bool> > > isSet(
327 std::vector<std::vector<bool> >(m_nY, std::vector<bool>(m_nZ,
false)));
329 std::ifstream infile;
330 infile.open(filename.c_str(), std::ios::in);
333 <<
" Could not open file " << filename <<
".\n";
337 std::transform(format.begin(), format.end(), format.begin(), toupper);
338 unsigned int fmt = 0;
339 if (format ==
"XY") {
341 }
else if (format ==
"XYZ") {
343 }
else if (format ==
"IJ") {
345 }
else if (format ==
"IJK") {
347 }
else if (format ==
"YXZ") {
351 <<
" Unknown format (" << format <<
").\n";
355 unsigned int nLines = 0;
358 while (std::getline(infile, line)) {
363 if (line.empty())
continue;
365 if (line[0] ==
'#')
continue;
366 if (line[0] ==
'/' && line[1] ==
'/')
continue;
375 std::istringstream data;
383 <<
" Error reading line " << nLines <<
".\n"
384 <<
" Cannot retrieve element coordinates.\n";
390 const double z = 0.5 * (m_zMin + m_zMax);
391 bool xMirrored, yMirrored, zMirrored;
392 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
394 <<
" Error reading line " << nLines <<
".\n"
395 <<
" Point is outside mesh.\n";
399 }
else if (fmt == 2) {
405 <<
" Error reading line " << nLines <<
".\n"
406 <<
" Cannot retrieve element coordinates.\n";
413 bool xMirrored, yMirrored, zMirrored;
414 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
416 <<
" Error reading line " << nLines <<
".\n"
417 <<
" Point is outside mesh.\n";
421 }
else if (fmt == 3) {
427 <<
" Error reading line " << nLines <<
".\n"
428 <<
" Cannot retrieve element index.\n";
432 }
else if (fmt == 4) {
437 <<
" Error reading line " << nLines <<
".\n"
438 <<
" Cannot retrieve element index.\n";
442 }
else if (fmt == 5) {
444 double x,
y,
z, temp;
445 data >>
y >>
x >> temp;
449 <<
" Error reading line " << nLines <<
".\n"
450 <<
" Cannot retrieve element coordinates.\n";
457 bool xMirrored, yMirrored, zMirrored;
458 if (!
GetElement(x, y, z, i, j, k, xMirrored, yMirrored, zMirrored)) {
460 <<
" Error reading line " << nLines <<
".\n"
461 <<
" Point is outside mesh.\n";
467 if (i >= m_nX || j >= m_nY || k >= m_nZ) {
469 <<
" Error reading line " << nLines <<
".\n"
470 <<
" Index (" << i <<
", " << j <<
", " << k
471 <<
") out of range.\n";
474 if (isSet[i][j][k]) {
476 <<
" Error reading line " << nLines <<
".\n"
477 <<
" Mesh element (" << i <<
", " << j <<
", " << k
478 <<
") has already been set.\n";
482 if (fmt == 1 || fmt == 3) {
486 }
else if (fmt == 5) {
488 data >> fy >> fx >> temp;
491 data >> fx >> fy >> fz;
495 <<
" Error reading line " << nLines <<
".\n"
496 <<
" Cannot read field values.\n";
507 <<
" Error reading line " << nLines <<
".\n"
508 <<
" Cannot read potential.\n";
513 if (m_pMin > m_pMax) {
518 if (v < m_pMin) m_pMin = v;
519 if (v > m_pMax) m_pMax = v;
526 <<
" Error reading line " << nLines <<
".\n"
527 <<
" Cannot read region.\n";
532 if (fmt == 1 || fmt == 3) {
534 for (
unsigned int kk = 0; kk < m_nZ; ++kk) {
535 fields[i][j][kk].fx = fx;
536 fields[i][j][kk].fy = fy;
537 fields[i][j][kk].fz = fz;
538 fields[i][j][kk].v = v;
539 if (withRegion) m_regions[i][j][kk] = region;
540 isSet[i][j][kk] =
true;
543 fields[i][j][k].fx = fx;
544 fields[i][j][k].fy = fy;
545 fields[i][j][k].fz = fz;
546 fields[i][j][k].v = v;
547 if (withRegion) m_regions[i][j][k] = region;
548 isSet[i][j][k] =
true;
552 if (bad)
return false;
554 <<
" Read " << nValues <<
" values from " << filename <<
".\n";
555 unsigned int nExpected = m_nX * m_nY;
556 if (fmt == 2 || fmt == 4 || fmt == 5) nExpected *= m_nZ;
557 if (nExpected != nValues) {
559 <<
" Expected " << nExpected <<
" values.\n";
565 double& xmax,
double& ymax,
double& zmax) {
594 double& xmin,
double& ymin,
double& zmin,
595 double& xmax,
double& ymax,
double& zmax) {
614 double& eymin,
double& eymax,
615 double& ezmin,
double& ezmax) {
617 std::cerr <<
m_className <<
"::GetElectricFieldRange:\n"
618 <<
" Field map is not ready for interpolation.\n";
622 exmin = exmax = m_efields[0][0][0].fx;
623 eymin = eymax = m_efields[0][0][0].fy;
624 ezmin = ezmax = m_efields[0][0][0].fz;
625 for (
unsigned int i = 0; i < m_nX; ++i) {
626 for (
unsigned int j = 0; j < m_nY; ++j) {
627 for (
unsigned int k = 0; k < m_nZ; ++k) {
628 const Element& element = m_efields[i][j][k];
629 if (element.fx < exmin) exmin = element.fx;
630 if (element.fx > exmax) exmax = element.fx;
631 if (element.fy < eymin) eymin = element.fy;
632 if (element.fy > eymax) eymax = element.fy;
633 if (element.fz < ezmin) ezmin = element.fz;
634 if (element.fz > ezmax) ezmax = element.fz;
645 <<
" Field map not yet initialised.\n";
649 if (m_media.empty()) {
650 std::cerr <<
m_className <<
"::PrintRegions: No regions defined.\n";
655 std::cout <<
" Index Medium\n";
656 const unsigned int nMedia = m_media.size();
657 for (
unsigned int i = 0; i < nMedia; ++i) {
658 const std::string name = m_media[i] ? m_media[i]->GetName() :
"none";
659 std::cout <<
" " << i <<
" " << name <<
"\n";
665 std::cerr <<
m_className <<
"::SetMedium: Null pointer.\n";
666 if (m_media.empty())
return;
668 if (i >= m_media.size()) m_media.resize(i + 1,
nullptr);
673 if (i >= m_media.size()) {
674 std::cerr <<
m_className <<
"::GetMedium: Index out of range.\n";
680bool ComponentVoxel::GetField(
681 const double xi,
const double yi,
const double zi,
682 const std::vector<std::vector<std::vector<Element> > >& field,
double& fx,
683 double& fy,
double& fz,
double& p,
int& region) {
685 std::cerr <<
m_className <<
"::GetField: Mesh is not set.\n";
691 bool xMirrored =
false;
694 if (x < m_xMin || x > m_xMax)
return false;
695 bool yMirrored =
false;
698 if (y < m_yMin || y > m_yMax)
return false;
699 bool zMirrored =
false;
702 if (z < m_zMin || z > m_zMax)
return false;
705 const double sx = (x - m_xMin) / m_dx;
706 const double sy = (y - m_yMin) / m_dy;
707 const double sz = (z - m_zMin) / m_dz;
708 unsigned int i =
static_cast<unsigned int>(sx);
709 unsigned int j =
static_cast<unsigned int>(sy);
710 unsigned int k =
static_cast<unsigned int>(sz);
711 if (i >= m_nX) i = m_nX - 1;
712 if (j >= m_nY) j = m_nY - 1;
713 if (k >= m_nZ) k = m_nZ - 1;
714 region = m_regions[i][j][k];
719 const double tx = sx - 0.5;
720 const double ty = sy - 0.5;
721 const double tz = sz - 0.5;
722 int i0 =
static_cast<int>(std::floor(tx));
723 int j0 =
static_cast<int>(std::floor(ty));
724 int k0 =
static_cast<int>(std::floor(tz));
728 unsigned int i1 = i0 + 1;
729 unsigned int j1 = j0 + 1;
730 unsigned int k1 = k0 + 1;
758 if (i1 >= m_nX) i1 = perx ? 0 : m_nX - 1;
759 if (j1 >= m_nY) j1 = pery ? 0 : m_nY - 1;
760 if (k1 >= m_nZ) k1 = perz ? 0 : m_nZ - 1;
761 const Element& n000 = field[i0][j0][k0];
762 const Element& n100 = field[i1][j0][k0];
763 const Element& n010 = field[i0][j1][k0];
764 const Element& n110 = field[i1][j1][k0];
765 const Element& n001 = field[i0][j0][k1];
766 const Element& n101 = field[i1][j0][k1];
767 const Element& n011 = field[i0][j1][k1];
768 const Element& n111 = field[i1][j1][k1];
770 const double ux = 1. - vx;
771 const double uy = 1. - vy;
772 const double uz = 1. - vz;
774 std::cout <<
m_className <<
"::GetField:\n Determining field at ("
775 << xi <<
", " << yi <<
", " << zi <<
").\n"
776 <<
" X: " << i0 <<
" (" << ux <<
") - "
777 << i1 <<
" (" << vx <<
").\n"
778 <<
" Y: " << j0 <<
" (" << uy <<
") - "
779 << j1 <<
" (" << vy <<
").\n"
780 <<
" Z: " << k0 <<
" (" << uz <<
") - "
781 << k1 <<
" (" << vz <<
").\n";
783 fx = ((n000.fx * ux + n100.fx * vx) * uy +
784 (n010.fx * ux + n110.fx * vx) * vy) *
786 ((n001.fx * ux + n101.fx * vx) * uy +
787 (n011.fx * ux + n111.fx * vx) * vy) *
789 fy = ((n000.fy * ux + n100.fy * vx) * uy +
790 (n010.fy * ux + n110.fy * vx) * vy) *
792 ((n001.fy * ux + n101.fy * vx) * uy +
793 (n011.fy * ux + n111.fy * vx) * vy) *
795 fz = ((n000.fz * ux + n100.fz * vx) * uy +
796 (n010.fz * ux + n110.fz * vx) * vy) *
798 ((n001.fz * ux + n101.fz * vx) * uy +
799 (n011.fz * ux + n111.fz * vx) * vy) *
801 p = ((n000.v * ux + n100.v * vx) * uy + (n010.v * ux + n110.v * vx) * vy) *
803 ((n001.v * ux + n101.v * vx) * uy + (n011.v * ux + n111.v * vx) * vy) *
806 const Element& element = field[i][j][k];
812 if (xMirrored) fx = -fx;
813 if (yMirrored) fy = -fy;
814 if (zMirrored) fz = -fz;
819 const double zi,
unsigned int& i,
820 unsigned int& j,
unsigned int& k,
821 bool& xMirrored,
bool& yMirrored,
822 bool& zMirrored)
const {
824 std::cerr <<
m_className <<
"::GetElement: Mesh is not set.\n";
832 if (x < m_xMin || x > m_xMax)
return false;
835 if (y < m_yMin || y > m_yMax)
return false;
838 if (z < m_zMin || z > m_zMax)
return false;
841 i = (
unsigned int)((x - m_xMin) / m_dx);
842 j = (
unsigned int)((y - m_yMin) / m_dy);
843 k = (
unsigned int)((z - m_zMin) / m_dz);
844 if (i >= m_nX) i = m_nX - 1;
845 if (j >= m_nY) j = m_nY - 1;
846 if (k >= m_nZ) k = m_nZ - 1;
851 const unsigned int k,
double& v,
double& ex,
852 double& ey,
double& ez)
const {
853 v = ex = ey = ez = 0.;
856 std::cerr <<
m_className <<
"::GetElement: Mesh not set.\n";
859 std::cerr <<
m_className <<
"::GetElement: Field map not set.\n";
862 if (i >= m_nX || j >= m_nY || k >= m_nZ) {
863 std::cerr <<
m_className <<
"::GetElement: Index out of range.\n";
866 const Element& element = m_efields[i][j][k];
874void ComponentVoxel::Reset() {
883 m_nX = m_nY = m_nZ = 0;
884 m_xMin = m_yMin = m_zMin = 0.;
885 m_xMax = m_yMax = m_zMax = 0.;
886 m_pMin = m_pMax = 0.;
890 m_hasPotential =
false;
896 m_wField_xOffset = 0.;
897 m_wField_yOffset = 0.;
898 m_wField_zOffset = 0.;
901void ComponentVoxel::UpdatePeriodicity() {
903 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
904 <<
" Field map not available.\n";
909 for (
unsigned int i = 0; i < 3; ++i) {
911 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
912 <<
" Both simple and mirror periodicity requested. Reset.\n";
918 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
919 <<
" Axial symmetry is not supported. Reset.\n";
925 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
926 <<
" Rotation symmetry is not supported. Reset.\n";
931double ComponentVoxel::Reduce(
const double xin,
const double xmin,
932 const double xmax,
const bool simplePeriodic,
933 const bool mirrorPeriodic,
bool& mirrored)
const {
936 const double lx = xmax - xmin;
937 if (simplePeriodic) {
938 x = xmin + fmod(x - xmin, lx);
939 if (x < xmin)
x += lx;
940 }
else if (mirrorPeriodic) {
941 double xNew = xmin + fmod(x - xmin, lx);
942 if (xNew < xmin) xNew += lx;
943 const int nx = int(floor(0.5 + (xNew - x) / lx));
944 if (nx != 2 * (nx / 2)) {
945 xNew = xmin + xmax - xNew;
953void ComponentVoxel::Initialise(
954 std::vector<std::vector<std::vector<Element> > >& fields) {
957 for (
unsigned int i = 0; i < m_nX; ++i) {
958 fields[i].resize(m_nY);
959 for (
unsigned int j = 0; j < m_nY; ++j) {
960 fields[i][j].resize(m_nZ);
961 for (
unsigned int k = 0; k < m_nZ; ++k) {
962 fields[i][j][k].fx = 0.;
963 fields[i][j][k].fy = 0.;
964 fields[i][j][k].fz = 0.;
965 fields[i][j][k].v = 0.;
971void ComponentVoxel::InitialiseRegions() {
972 if (!m_hasMesh)
return;
973 m_regions.resize(m_nX);
974 for (
unsigned int i = 0; i < m_nX; ++i) {
975 m_regions[i].resize(m_nY);
976 for (
unsigned int j = 0; j < m_nY; ++j) {
977 m_regions[i][j].assign(m_nZ, 0);
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].
bool GetElementaryCell(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the coordinates of the elementary cell.
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 components.
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
bool m_debug
Switch on/off debugging messages.
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
std::string m_className
Class name.
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_axiallyPeriodic
Axial periodicity in x, y, z.
Abstract base class for media.
void ltrim(std::string &line)