00001 // BLCoil.hh 00002 /* 00003 This source file is part of G4beamline, http://g4beamline.muonsinc.com 00004 Copyright (C) 2003,2004,2005,2006 by Tom Roberts, all rights reserved. 00005 00006 This program is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU General Public License 00008 as published by the Free Software Foundation; either version 2 00009 of the License, or (at your option) any later version. 00010 00011 This program is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 http://www.gnu.org/copyleft/gpl.html 00017 */ 00018 00019 #ifndef BLCOIL_HH 00020 #define BLCOIL_HH 00021 00022 #include <map> 00023 00024 #include "globals.hh" 00025 #include "BLCommand.hh" 00026 #ifdef STUB 00027 #include "NTuple.hh" 00028 #endif // STUB 00029 00030 /** BLCoil creates and implements a coil (part of a solenoid) 00031 * 00032 * A BLCoil consists only of the parameters describing the coil geometry 00033 * and a field map for unit current density. It is not a BLElement, and 00034 * cannot be placed geometrically -- see BLSolenoid for that. 00035 * 00036 * Cylindrical symmetry around z is assumed. The field is approximated 00037 * by computing the field from nSheets infinitesimally-thin current 00038 * sheets interpolating from innerRadius to OuterRadius, each length 00039 * long. The solution to Maxwell's Equations for each sheet is used. 00040 * 00041 * The field has been verified to be correct within 0.2% for both a 00042 * long solenoid and for a pair of Helmholtz coils. 00043 * 00044 * Effect of nSheets on accuracy: consider the coil innerRadius=330.0*mm, 00045 * outerRadius=505.0*mm, length=167.0*mm. Compute the maximum error 00046 * for 1000 random points, excluding the region within 2 cm of the coils; 00047 * use 99 sheets as the "correct" value. In the region 0<=r<2*outerRadius, 00048 * 0<z<10*outerRadius, for 0.1% accuracy one needs 11 sheets, and for 1% 00049 * one needs 4 sheets. In the region 0<=r<innerRadius/2, same z, for 0.1% 00050 * accuracy one needs 5 sheets, and for 1% one needs 2 sheets. Note that 00051 * inside the coils this approximation of using thin sheets is not very 00052 * good; but interpolating on a grid and coordinating sheet positions 00053 * with the grid is probably about as good as one can do -- at least the 00054 * values will be smooth (which is not true for the bare computation -- 00055 * there's a singularity at every sheet, and Bz jumps from +infinity 00056 * to -infinity across the sheet). 00057 * 00058 * Note that Bz does not decrease to 0.1% of its central value until 00059 * z>10*(innerRadius+outerRadius)/2; it decreases to 1% at ~5 times that 00060 * average radius. 00061 * 00062 * 00063 * BLCoil can also read a mapFile defining the field (rather than doing 00064 * the above computation). For instance, it could be from opera-2d. 00065 * The mapFile gives tables of Bz and Br on a grid in the z-r plane. 00066 * mapFile format: 00067 * lines beginning with # are comments, and are ignored. 00068 * The first line contains the parameters in name=value form: 00069 * z0 initial value of z (mm) 00070 * dz grid interval along z (mm) 00071 * nz number of grid points along z 00072 * dr grid interval along r (mm) 00073 * nr number of grid points along r 00074 * reflectZ set nonzero to reflect in the z=0 plane 00075 * (inverting Br) 00076 * current the current for this field (A/mm^2) 00077 * All parameters are required. If reflectZ is nonzero then 00078 * z0 must be 0.0 (only z>=0 is contained in the file). 00079 * The point with local coordinates (z=0,r=0) is what gets placed. 00080 * 00081 * Then follow the two maps in any order. Each map begins 00082 * with a line giving its name (Bz or Br) in column 1, followed 00083 * by nz lines each containing nr floats (Tesla). The first 00084 * value in each line is for r=0. Values are separated by one 00085 * or more spaces (the first value on each line may but need not 00086 * be preceeded by spaces). 00087 * The main reason to use a mapFile is to accurately include the effects 00088 * of iron. That implies that the map may not be accurate for values 00089 * of current other than that for which the map was generated. 00090 * 00091 * See BLCMDfieldmap for a more general EM field read from a map-file. 00092 * 00093 * 00094 * BLCoil was separated from BLSolenoid so multiple solenoids with the 00095 * same geometry (coil) could share a single (large) field map for the 00096 * coil -- the B field is scaled by current/currentOfMap, where 00097 * currentOfMap is 1.0 for the computed field, and is the current 00098 * parameter from a mapFile. 00099 * 00100 * This class does not use BLFieldMap becuase it predates it, and 00101 * because the map must include the parameters so construct() will 00102 * know whether or not the map corresponds to the current values. 00103 * You can, of course, build a solenoid out of a BLTubs and a 00104 * BLFieldMap. 00105 **/ 00106 class BLCoil { 00107 static std::map<G4String,BLCoil*> mapCoil; 00108 G4String name; 00109 G4double innerRadius; 00110 G4double outerRadius; 00111 G4double length; 00112 G4int nSheets; 00113 G4String material; 00114 G4double tolerance; 00115 G4double maxR; 00116 G4double minZ; 00117 G4double maxZ; 00118 G4double dR; 00119 G4double dZ; 00120 G4int nR; 00121 G4int nZ; 00122 G4String filename; 00123 G4String mapFile; 00124 G4int exactComputation; 00125 float *mapBr; 00126 float *mapBz; 00127 G4double sheetR0; 00128 G4double sheetDR; 00129 G4double norm; 00130 G4bool goodMap; 00131 G4bool reflectZ; 00132 G4double currentOfMap; 00133 friend class BLCMDcoil; 00134 friend class BLCMDsolenoid; 00135 friend class SolenoidField; 00136 public: 00137 /// Default constructor. 00138 BLCoil(); 00139 00140 /// Complex Constructor. All arguments that default to 0 are related 00141 /// to generating the field map; those that are 0 will be determined 00142 /// by tolerance. To construct the map the minimum info is tolerance 00143 /// and maxR; if either of them is 0, no map will be generated or used. 00144 /// NOTE: without a map, field values inside the coils will fluctuate 00145 /// wildly, because points on the sheets are singular; map construction 00146 /// is designed to minimize this. Without a map, nSheets must be given. 00147 /// tolerance is a fraction of the best value at r=0,z=0; values 00148 /// smaller than about 0.001 are unrealistic (and expensive!). 00149 BLCoil(G4String _name, G4double _innerRadius, G4double _outerRadius, 00150 G4double _length, G4int _nSheets=0, G4double _tolerance=0.0, 00151 G4double _maxR=0.0, G4double _maxZ=0.0, G4double _dR=0.0, 00152 G4double _dZ=0.0, G4int _nR=0, G4int _nZ=0, 00153 G4String _filename="") { 00154 name=_name; innerRadius=_innerRadius; 00155 outerRadius=_outerRadius; length=_length; 00156 nSheets=_nSheets; tolerance=_tolerance; maxR=_maxR; 00157 maxZ=_maxZ; dR=_dR; dZ=_dZ; nR=_nR; nZ=_nZ; 00158 filename = _filename; 00159 exactComputation = 0; 00160 mapBr=0; mapBz=0; sheetR0=0.0; sheetDR=0.0; norm=0.0; 00161 goodMap=false; 00162 if(nSheets > 0) { 00163 sheetDR=(outerRadius-innerRadius)/nSheets; 00164 sheetR0=innerRadius + sheetDR/2.0; 00165 } 00166 if(filename == "") filename = name + ".dat"; 00167 } 00168 00169 /// Destructor. 00170 virtual ~BLCoil(); 00171 00172 /// Copy constructor. 00173 BLCoil(const BLCoil& r); 00174 00175 /// printCoil() will print a description 00176 void printCoil(); 00177 00178 /// find() returns a pointer to the named BLCoil. 00179 static BLCoil *find(G4String name); 00180 00181 /// addField() adds the field value for the given point[] into field[]. 00182 /// Note that point[] is relative to the center of the coil. 00183 /// Units for current are Amp/mm^2. 00184 void addField(const G4double point[4], G4double field[6], 00185 G4double currentAmpPerMm2) const; 00186 00187 /// generateFieldMap() will generate the field map (for execution 00188 /// speedup). If just maxR and tolerance were given, all other 00189 /// map parameters will be automatically determined so that the 00190 /// estimated error is less than the specified tolerance. 00191 /// The field map is cached in a disk file which is automatically 00192 /// recreated if anything changes (may take several minutes). 00193 void generateFieldMap(); 00194 00195 /// readMapFile() will read the mapFile. 00196 void readMapFile(); 00197 00198 /// displayMapValues() will display the 4 map values around a point. 00199 void displayMapValues(G4double r, G4double z); 00200 private: 00201 /// getUnitField() will return Br and Bz for unit current. 00202 void getUnitField(G4double r, G4double z, G4double& Br, G4double& Bz); 00203 00204 /// determineMaxR() will determine maxR based on tolerance. 00205 /// Uses nSheets=99 to determine center field, and walks out 00206 /// far enough so (Bz/Bz0)<0.5*tolerance. 00207 void determineMaxR(); 00208 00209 /// determineMaxZ() will determine maxZ based on tolerance. 00210 /// Uses nSheets=99 to determine on-axis field, and walks out 00211 /// far enough so (Bz/Bz0)<0.5*tolerance. 00212 void determineMaxZ(); 00213 00214 /// determineNsheets() will determine nSheets, given maxZ and 00215 /// tolerance. Increases nSheets until maxError < tolerance/4 00216 /// (the other 3/4 of tolerance is for mapping errors). Checks 00217 /// just a handful of points selected to be representative. 00218 void determineNsheets(); 00219 00220 /// estimateMapError() will estimate the maximum error in mapping, 00221 /// given maxZ, nSheets, dR, dZ, nR, nZ, and an allocated but 00222 /// uninitialized fieldMap. It generates 1000 random points and 00223 /// returns the maximum error; quits early if tolerance is 00224 /// exceeded. Error is (currentMap - best)/best(r=0,z=0), where 00225 /// best is computed with no map and nSheets=99. For each point 00226 /// it generates the 4 surrounding values of fieldMap (this is faster 00227 /// than generating the entire map first). 00228 /// Returns the maximum error of either Bz or Br, normalized to 00229 /// Bz(r=0,z=0,nSheets=99). 00230 G4double estimateMapError(); 00231 00232 /// computeError() computes the error for a single point. 00233 /// Error is (currentMap - best)/best(r=0,z=0), where best should be 00234 /// an identical BLCoil with no map and nSheets=99. If makeMap is true, 00235 /// it generates the 4 surrounding values of the fieldMap. 00236 G4double computeError(G4double r, G4double z, BLCoil &best, 00237 G4bool makeMap); 00238 00239 /// getSheetField() returns the B field from an infinitesimally-thin 00240 /// sheet of current. Arguments: 00241 /// r and z are the cylindrical coordinates of the point in question 00242 /// relative to center of the coil (axis along z), mm. 00243 /// Br and Bz return the computed B field at that point (geant4 units) 00244 /// sheetR is the radius of the current sheet (mm) 00245 /// sheetLen is the length of the current sheet (mm) 00246 /// currentAmpPerMm is the current of the sheet (Amp per linear mm) 00247 void getSheetField(G4double r, G4double z, G4double& Br, G4double& Bz, 00248 G4double sheetR, G4double sheetLen, G4double currentAmpPerMm) 00249 const; 00250 }; 00251 00252 #endif // BLCOIL_HH