OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
OpalElement.cpp
Go to the documentation of this file.
1//
2// Class OpalElement
3// Base class for all beam line elements.
4//
5// Copyright (c) 200x - 2020, Paul Scherrer Institut, Villigen PSI, Switzerland
6// All rights reserved
7//
8// This file is part of OPAL.
9//
10// OPAL is free software: you can redistribute it and/or modify
11// it under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// You should have received a copy of the GNU General Public License
16// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
17//
19
20#include "AbsBeamline/Bend2D.h"
25#include "Parser/Statement.h"
26#include "Physics/Physics.h"
28#include "Utilities/Options.h"
30#include "Utilities/Util.h"
31
32#include <cmath>
33#include <exception>
34#include <ostream>
35#include <regex>
36#include <sstream>
37#include <string>
38#include <utility>
39#include <vector>
40
41OpalElement::OpalElement(int size, const char* name, const char* help):
42 Element(size, name, help), itsSize(size) {
44 ("TYPE", "The element design type.",
45 {"RING",
46 "CARBONCYCL",
47 "CYCIAE",
48 "AVFEQ",
49 "FFA",
50 "BANDRF",
51 "SYNCHROCYCLOTRON",
52 "SINGLEGAP",
53 "STANDING",
54 "TEMPORAL",
55 "SPATIAL"});
56
58 ("L", "The element length [m]");
59
61 ("ELEMEDGE", "The position of the element in path length [m]");
62
64 ("APERTURE", "The element aperture");
65
67 ("WAKEF", "Defines the wake function");
68
70 ("PARTICLEMATTERINTERACTION", "Defines the particle mater interaction handler");
71
73 ("ORIGIN", "The location of the element");
74
76 ("ORIENTATION", "The Tait-Bryan angles for the orientation of the element");
77
79 ("X", "The x-coordinate of the location of the element", 0);
80
82 ("Y", "The y-coordinate of the location of the element", 0);
83
85 ("Z", "The z-coordinate of the location of the element", 0);
86
88 ("THETA", "The rotation about the y-axis of the element", 0);
89
91 ("PHI", "The rotation about the x-axis of the element", 0);
92
94 ("PSI", "The rotation about the z-axis of the element", 0);
95
97 ("DX", "Misalignment in x direction", 0.0);
98
100 ("DY", "Misalignment in y direction", 0.0);
101
103 ("DZ", "Misalignment in z direction", 0.0);
104
106 ("DTHETA", "Misalignment in theta (Tait-Bryan angles)", 0.0);
107
109 ("DPHI", "Misalignment in theta (Tait-Bryan angles)", 0.0);
110
112 ("DPSI", "Misalignment in theta (Tait-Bryan angles)", 0.0);
113
115 ("OUTFN", "Output filename");
116
118 ("DELETEONTRANSVERSEEXIT", "Flag controlling if particles should be deleted if they exit "
119 "the element transversally. Default=TRUE", true);
120
121 const unsigned int end = COMMON;
122 for (unsigned int i = 0; i < end; ++ i) {
124 }
125}
126
127
128OpalElement::OpalElement(const std::string& name, OpalElement* parent):
129 Element(name, parent), itsSize(parent->itsSize)
130{}
131
132
135
136
137std::pair<ApertureType, std::vector<double> > OpalElement::getApert() const {
138
139 std::pair<ApertureType, std::vector<double> > retvalue(ApertureType::ELLIPTICAL,
140 std::vector<double>({0.5, 0.5, 1.0}));
141 if (!itsAttr[APERT]) return retvalue;
142
143 std::string aperture = Attributes::getString(itsAttr[APERT]);
144
145 std::regex square("square *\\((.*)\\)", std::regex::icase);
146 std::regex rectangle("rectangle *\\((.*)\\)", std::regex::icase);
147 std::regex circle("circle *\\((.*)\\)", std::regex::icase);
148 std::regex ellipse("ellipse *\\((.*)\\)", std::regex::icase);
149
150 std::regex twoArguments("^\\s*([^,]+)\\s*,\\s*([^,]+)\\s*$");
151 std::regex threeArguments("^\\s*([^,]+)\\s*,\\s*([^,]+)\\s*,\\s*([^,]+)\\s*$");
152
153 std::smatch match;
154
155 const double width2HalfWidth = 0.5;
156
157 auto validateConicScale = [&](double scale, const std::string& arguments) {
158 if (!(scale > 0.0)) {
159 throw OpalException("OpalElement::getApert()",
160 "invalid conic aperture scale in '" + arguments +
161 "': expected positive real value");
162 }
163 };
164
165 if (std::regex_search(aperture, match, square)) {
166 std::string arguments = match[1];
167 if (!std::regex_search(arguments, match, twoArguments)) {
168 retvalue.first = ApertureType::RECTANGULAR;
169
170 try {
171 retvalue.second[0] = width2HalfWidth * std::stod(arguments);
172 retvalue.second[1] = retvalue.second[0];
173 } catch (const std::exception &ex) {
174 throw OpalException("OpalElement::getApert()",
175 "could not convert '" + arguments + "' to double");
176 }
177
178 } else {
179 retvalue.first = ApertureType::CONIC_RECTANGULAR;
180
181 try {
182 retvalue.second[0] = width2HalfWidth * std::stod(match[1]);
183 retvalue.second[1] = retvalue.second[0];
184 retvalue.second[2] = std::stod(match[2]);
185 } catch (const std::exception &ex) {
186 throw OpalException("OpalElement::getApert()",
187 "could not convert '" + arguments + "' to doubles");
188 }
189 validateConicScale(retvalue.second[2], arguments);
190 }
191
192 return retvalue;
193 }
194
195 if (std::regex_search(aperture, match, rectangle)) {
196 std::string arguments = match[1];
197
198 if (!std::regex_search(arguments, match, threeArguments)) {
199 retvalue.first = ApertureType::RECTANGULAR;
200
201 try {
202 size_t sz = 0;
203 retvalue.second[0] = width2HalfWidth * std::stod(arguments, &sz);
204 sz = arguments.find_first_of(",", sz) + 1;
205 retvalue.second[1] = width2HalfWidth * std::stod(arguments.substr(sz));
206 } catch (const std::exception &ex) {
207 throw OpalException("OpalElement::getApert()",
208 "could not convert '" + arguments + "' to doubles");
209 }
210
211 } else {
212 retvalue.first = ApertureType::CONIC_RECTANGULAR;
213 try {
214 retvalue.second[0] = width2HalfWidth * std::stod(match[1]);
215 retvalue.second[1] = width2HalfWidth * std::stod(match[2]);
216 retvalue.second[2] = std::stod(match[3]);
217 } catch (const std::exception &ex) {
218 throw OpalException("OpalElement::getApert()",
219 "could not convert '" + arguments + "' to doubles");
220 }
221 validateConicScale(retvalue.second[2], arguments);
222 }
223
224 return retvalue;
225 }
226
227 if (std::regex_search(aperture, match, circle)) {
228 std::string arguments = match[1];
229 if (!std::regex_search(arguments, match, twoArguments)) {
230 retvalue.first = ApertureType::ELLIPTICAL;
231 try {
232 retvalue.second[0] = width2HalfWidth * std::stod(arguments);
233 retvalue.second[1] = retvalue.second[0];
234 } catch (const std::exception &ex) {
235 throw OpalException("OpalElement::getApert()",
236 "could not convert '" + arguments + "' to double");
237 }
238
239 } else {
240 retvalue.first = ApertureType::CONIC_ELLIPTICAL;
241 try {
242 retvalue.second[0] = width2HalfWidth * std::stod(match[1]);
243 retvalue.second[1] = retvalue.second[0];
244 retvalue.second[2] = std::stod(match[2]);
245 } catch (const std::exception &ex) {
246 throw OpalException("OpalElement::getApert()",
247 "could not convert '" + arguments + "' to doubles");
248 }
249 validateConicScale(retvalue.second[2], arguments);
250 }
251
252 return retvalue;
253 }
254
255 if (std::regex_search(aperture, match, ellipse)) {
256 std::string arguments = match[1];
257
258 if (!std::regex_search(arguments, match, threeArguments)) {
259 retvalue.first = ApertureType::ELLIPTICAL;
260 try {
261 size_t sz = 0;
262
263 retvalue.second[0] = width2HalfWidth * std::stod(arguments, &sz);
264 sz = arguments.find_first_of(",", sz) + 1;
265 retvalue.second[1] = width2HalfWidth * std::stod(arguments.substr(sz));
266
267 } catch (const std::exception &ex) {
268 throw OpalException("OpalElement::getApert()",
269 "could not convert '" + arguments + "' to doubles");
270 }
271
272 } else {
273 retvalue.first = ApertureType::CONIC_ELLIPTICAL;
274 try {
275 retvalue.second[0] = width2HalfWidth * std::stod(match[1]);
276 retvalue.second[1] = width2HalfWidth * std::stod(match[2]);
277 retvalue.second[2] = std::stod(match[3]);
278 } catch (const std::exception &ex) {
279 throw OpalException("OpalElement::getApert()",
280 "could not convert '" + arguments + "' to doubles");
281 }
282 validateConicScale(retvalue.second[2], arguments);
283 }
284
285 return retvalue;
286 }
287
288 if (!aperture.empty()) {
289 throw OpalException("OpalElement::getApert()",
290 "Unknown aperture type '" + aperture + "'.");
291 }
292
293 return retvalue;
294}
295
298}
299
300
301const std::string OpalElement::getTypeName() const {
302 const Attribute* attr = findAttribute("TYPE");
303 return attr ? Attributes::getString(*attr) : std::string();
304}
305
309const std::string OpalElement::getWakeF() const {
310 const Attribute* attr = findAttribute("WAKEF");
311 return attr ? Attributes::getString(*attr) : std::string();
312}
313
315 const Attribute* attr = findAttribute("PARTICLEMATTERINTERACTION");
316 return attr ? Attributes::getString(*attr) : std::string();
317}
318
320 while (stat.delimiter(',')) {
321 std::string name = Expressions::parseString(stat, "Attribute name expected.");
323
324 if (attr == 0) {
325 throw OpalException("OpalElement::parse",
326 "unknown attribute \"" + name + "\"");
327 }
328
329 if (stat.delimiter('[')) {
330 int index = int(std::round(Expressions::parseRealConst(stat)));
332
333 if (stat.delimiter('=')) {
334 attr->parseComponent(stat, true, index);
335 } else if (stat.delimiter(":=")) {
336 attr->parseComponent(stat, false, index);
337 } else {
338 throw ParseError("OpalElement::parse()",
339 "Delimiter \"=\" or \":=\" expected.");
340 }
341 } else {
342 if (stat.delimiter('=')) {
343 attr->parse(stat, true);
344 } else if (stat.delimiter(":=")) {
345 attr->parse(stat, false);
346 } else {
347 attr->setDefault();
348 }
349 }
350 }
351}
352
353
354void OpalElement::print(std::ostream& os) const {
355 std::string head = getOpalName();
356
357 Object* parent = getParent();
358 if (parent != 0 && ! parent->getOpalName().empty()) {
359 if (! getOpalName().empty()) head += ':';
360 head += parent->getOpalName();
361 }
362
363 os << head;
364 os << ';'; // << "JMJdebug OPALElement.cc" ;
365 os << std::endl;
366}
367
368
370 int order, int& len,
371 const std::string& sName,
372 const std::string& tName,
373 const Attribute& length,
374 const Attribute& sNorm,
375 const Attribute& sSkew) {
376 // Find out which type of output is required.
377 int flag = 0;
378 if (sNorm) {
379 if (sNorm.getBase().isExpression()) {
380 flag += 2;
381 } else if (Attributes::getReal(sNorm) != 0.0) {
382 flag += 1;
383 }
384 }
385
386 if (sSkew) {
387 if (sSkew.getBase().isExpression()) {
388 flag += 6;
389 } else if (Attributes::getReal(sSkew) != 0.0) {
390 flag += 3;
391 }
392 }
393 // cout << "JMJdebug, OpalElement.cc: flag=" << flag << endl ;
394 // Now do the output.
395 int div = 2 * (order + 1);
396
397 switch (flag) {
398
399 case 0:
400 // No component at all.
401 break;
402
403 case 1:
404 case 2:
405 // Pure normal component.
406 {
407 std::string normImage = sNorm.getImage();
408 if (length) {
409 normImage = "(" + normImage + ")*(" + length.getImage() + ")";
410 }
411 printAttribute(os, sName, normImage, len);
412 }
413 break;
414
415 case 3:
416 case 6:
417 // Pure skew component.
418 {
419 std::string skewImage = sSkew.getImage();
420 if (length) {
421 skewImage = "(" + skewImage + ")*(" + length.getImage() + ")";
422 }
423 printAttribute(os, sName, skewImage, len);
424 double tilt = Physics::pi / double(div);
425 printAttribute(os, tName, tilt, len);
426 }
427 break;
428
429 case 4:
430 // Both components are non-zero constants.
431 {
432 double sn = Attributes::getReal(sNorm);
433 double ss = Attributes::getReal(sSkew);
434 double strength = std::sqrt(sn * sn + ss * ss);
435 if (strength) {
436 std::ostringstream ts;
437 ts << strength;
438 std::string image = ts.str();
439 if (length) {
440 image = "(" + image + ")*(" + length.getImage() + ")";
441 }
442 printAttribute(os, sName, image, len);
443 double tilt = - std::atan2(ss, sn) / double(div);
444 if (tilt) printAttribute(os, tName, tilt, len);
445 }
446 }
447 break;
448
449 case 5:
450 case 7:
451 case 8:
452 // One or both components is/are expressions.
453 {
454 std::string normImage = sNorm.getImage();
455 std::string skewImage = sSkew.getImage();
456 std::string image =
457 "SQRT((" + normImage + ")^2+(" + skewImage + ")^2)";
458 printAttribute(os, sName, image, len);
459 if (length) {
460 image = "(" + image + ")*(" + length.getImage() + ")";
461 }
462 std::string divisor;
463 if (div < 9) {
464 divisor = "0";
465 divisor[0] += div;
466 } else {
467 divisor = "00";
468 divisor[0] += div / 10;
469 divisor[1] += div % 10;
470 }
471 image = "-ATAN2(" + skewImage + ',' + normImage + ")/" + divisor;
472 printAttribute(os, tName, image, len);
473 break;
474 }
475 }
476}
477
479 ElementBase* base = getElement();
480
481 auto apert = getApert();
482 base->setAperture(apert.first, apert.second);
483
485 std::vector<double> ori = Attributes::getRealArray(itsAttr[ORIGIN]);
486 std::vector<double> dir = Attributes::getRealArray(itsAttr[ORIENTATION]);
487 Vector_t origin(0.0);
488 Quaternion rotation;
489
490 if (dir.size() == 3) {
491 Quaternion rotTheta(std::cos(0.5 * dir[0]), 0, std::sin(0.5 * dir[0]), 0);
492 Quaternion rotPhi(std::cos(0.5 * dir[1]), std::sin(0.5 * dir[1]), 0, 0);
493 Quaternion rotPsi(std::cos(0.5 * dir[2]), 0, 0, std::sin(0.5 * dir[2]));
494 rotation = rotTheta * (rotPhi * rotPsi);
495 } else {
496 if (itsAttr[ORIENTATION]) {
497 throw OpalException("OpalElement::update",
498 "Parameter orientation is array of 3 values (theta, phi, psi);\n" +
499 std::to_string(dir.size()) + " values provided");
500 }
501 }
502
503 if (ori.size() == 3) {
504 origin = Vector_t({ori[0], ori[1], ori[2]});
505 } else {
506 if (itsAttr[ORIGIN]) {
507 throw OpalException("OpalElement::update",
508 "Parameter origin is array of 3 values (x, y, z);\n" +
509 std::to_string(ori.size()) + " values provided");
510 }
511 }
512
513 CoordinateSystemTrafo global2local(origin,
514 rotation.conjugate());
515 base->setCSTrafoGlobal2Local(global2local);
516 base->fixPosition();
517
518 } else if (!itsAttr[PSI].defaultUsed() &&
519 itsAttr[X].defaultUsed() &&
520 itsAttr[Y].defaultUsed() &&
521 itsAttr[Z].defaultUsed() &&
522 itsAttr[THETA].defaultUsed() &&
523 itsAttr[PHI].defaultUsed()) {
525 } else if (!itsAttr[X].defaultUsed() ||
526 !itsAttr[Y].defaultUsed() ||
527 !itsAttr[Z].defaultUsed() ||
528 !itsAttr[THETA].defaultUsed() ||
529 !itsAttr[PHI].defaultUsed() ||
530 !itsAttr[PSI].defaultUsed()) {
531 const Vector_t origin({Attributes::getReal(itsAttr[X]),
534
535 const double theta = Attributes::getReal(itsAttr[THETA]);
536 const double phi = Attributes::getReal(itsAttr[PHI]);
537 const double psi = Attributes::getReal(itsAttr[PSI]);
538
539 Quaternion rotTheta(std::cos(0.5 * theta), 0, std::sin(0.5 * theta), 0);
540 Quaternion rotPhi(std::cos(0.5 * phi), std::sin(0.5 * phi), 0, 0);
541 Quaternion rotPsi(std::cos(0.5 * psi), 0, 0, std::sin(0.5 * psi));
542 Quaternion rotation = rotTheta * (rotPhi * rotPsi);
543
544 CoordinateSystemTrafo global2local(origin,
545 rotation.conjugate());
546 base->setCSTrafoGlobal2Local(global2local);
547 base->fixPosition();
549 }
550
551 Vector_t misalignmentShift({Attributes::getReal(itsAttr[DX]),
554 double dtheta = Attributes::getReal(itsAttr[DTHETA]);
555 double dphi = Attributes::getReal(itsAttr[DPHI]);
556 double dpsi = Attributes::getReal(itsAttr[DPSI]);
557 Quaternion rotationY(std::cos(0.5 * dtheta), 0, std::sin(0.5 * dtheta), 0);
558 Quaternion rotationX(std::cos(0.5 * dphi), std::sin(0.5 * dphi), 0, 0);
559 Quaternion rotationZ(std::cos(0.5 * dpsi), 0, 0, std::sin(0.5 * dpsi));
560 Quaternion misalignmentRotation = rotationY * rotationX * rotationZ;
561 CoordinateSystemTrafo misalignment(misalignmentShift,
562 misalignmentRotation.conjugate());
563
564 base->setMisalignment(misalignment);
565
566 if (itsAttr[ELEMEDGE])
568
570}
571
573 for (std::vector<Attribute>::size_type i = itsSize;
574 i < itsAttr.size(); ++i) {
575 Attribute &attr = itsAttr[i];
576 base->setAttribute(attr.getName(), Attributes::getReal(attr));
577
578 }
579}
580
581void OpalElement::printAttribute(std::ostream& os, const std::string& name,
582 const std::string& image, int& len) {
583 len += name.length() + image.length() + 2;
584 if (len > 74) {
585 os << ",&\n ";
586 len = name.length() + image.length() + 3;
587 } else {
588 os << ',';
589 }
590 os << name << '=' << image;
591}
592
594(std::ostream &os, const std::string &name, double value, int &len) {
595 std::ostringstream ss;
596 ss << value << std::ends;
597 printAttribute(os, name, ss.str(), len);
598}
599
600
602 if (getParent() != 0) return;
603
604 const unsigned int end = itsSize;
605 const std::string name = getOpalName();
606 for (unsigned int i = COMMON; i < end; ++ i) {
608 }
609}
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
const std::string name
std::string parseString(Statement &, const char msg[])
Parse string value.
void parseDelimiter(Statement &stat, char delim)
Test for one-character delimiter.
double parseRealConst(Statement &)
Parse real constant.
Attribute makeBool(const std::string &name, const std::string &help)
Make logical attribute.
double getReal(const Attribute &attr)
Return real value.
Attribute makePredefinedString(const std::string &name, const std::string &help, const std::initializer_list< std::string > &predefinedStrings)
Make predefined string attribute.
Attribute makeReal(const std::string &name, const std::string &help)
Make real attribute.
bool getBool(const Attribute &attr)
Return logical value.
Attribute makeRealArray(const std::string &name, const std::string &help)
Create real array attribute.
std::vector< double > getRealArray(const Attribute &attr)
Get array value.
std::string getString(const Attribute &attr)
Get string value.
Attribute makeString(const std::string &name, const std::string &help)
Make string attribute.
constexpr double pi
The value of.
Definition Physics.h:30
A representation of an Object attribute.
Definition Attribute.h:52
AttributeBase & getBase() const
Return reference to polymorphic value.
Definition Attribute.cpp:69
const std::string & getName() const
Return the attribute name.
Definition Attribute.cpp:92
void setDefault()
Assign default value.
void parse(Statement &stat, bool eval)
Parse attribute.
void parseComponent(Statement &stat, bool eval, int index)
Parse array component.
std::string getImage() const
Return printable representation.
Definition Attribute.cpp:87
virtual bool isExpression() const
Test for expression.
static void addAttributeOwner(const std::string &owner, const OwnerType &type, const std::string &name)
ElementBase * getElement() const
Return the embedded CLASSIC element.
Definition Element.h:120
The base class for all OPAL objects.
Definition Object.h:48
Object * getParent() const
Return parent pointer.
Definition Object.cpp:315
const std::string & getOpalName() const
Return object name.
Definition Object.cpp:310
virtual Attribute * findAttribute(const std::string &name)
Find an attribute by name.
Definition Object.cpp:64
std::vector< Attribute > itsAttr
The object attributes.
Definition Object.h:216
void setElementPosition(double elemedge)
Access to ELEMEDGE attribute.
void fixPosition()
void setAperture(const ApertureType &type, const std::vector< double > &args)
void setMisalignment(const CoordinateSystemTrafo &cst)
virtual void setAttribute(const std::string &aKey, double val)
Set value of an attribute.
void setFlagDeleteOnTransverseExit(bool=true)
void setRotationAboutZ(double rotation)
Set rotation about z axis in bend frame.
void setCSTrafoGlobal2Local(const CoordinateSystemTrafo &ori)
Quaternion conjugate() const
Definition Quaternion.h:103
Interface for statements.
Definition Statement.h:38
bool delimiter(char c)
Test for delimiter.
Parse exception.
Definition ParseError.h:32
std::pair< ApertureType, std::vector< double > > getApert() const
static void printMultipoleStrength(std::ostream &os, int order, int &len, const std::string &sName, const std::string &tName, const Attribute &length, const Attribute &vNorm, const Attribute &vSkew)
Print multipole components in OPAL-8 format.
virtual double getLength() const
Return element length.
static void printAttribute(std::ostream &os, const std::string &name, const std::string &image, int &len)
Print an attribute with a OPAL-8 name (as an expression).
@ DELETEONTRANSVERSEEXIT
Definition OpalElement.h:58
@ PARTICLEMATTERINTERACTION
Definition OpalElement.h:42
virtual void parse(Statement &)
Parse the element.
const std::string getParticleMatterInteraction() const
const std::string getWakeF() const
Return the element's type name.
virtual void updateUnknown(ElementBase *)
Transmit the `‘unknown’' (not known to OPAL) attributes to CLASSIC.
const std::string getTypeName() const
Return the element's type name.
virtual void print(std::ostream &) const
Print the object.
virtual ~OpalElement()
virtual void update()
Update the embedded CLASSIC element.
void registerOwnership() const
The base class for all OPAL exceptions.
Vektor< double, 3 > Vector_t
Definition Vektor.h:6