Changeset 1230 for trunk/examples/extended/electromagnetic/TestEm7/include
- Timestamp:
- Jan 8, 2010, 3:02:48 PM (14 years ago)
- Location:
- trunk/examples/extended/electromagnetic/TestEm7/include
- Files:
-
- 22 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/examples/extended/electromagnetic/TestEm7/include/DetectorConstruction.hh
r807 r1230 25 25 // 26 26 // $Id: DetectorConstruction.hh,v 1.2 2006/06/29 16:57:29 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/DetectorMessenger.hh
r807 r1230 25 25 // 26 26 // $Id: DetectorMessenger.hh,v 1.3 2006/06/29 16:57:31 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/EventAction.hh
r807 r1230 25 25 // 26 26 // $Id: EventAction.hh,v 1.2 2006/06/29 16:57:34 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/EventActionMessenger.hh
r807 r1230 25 25 // 26 26 // $Id: EventActionMessenger.hh,v 1.3 2006/06/29 16:57:36 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/G4ScreenedNuclearRecoil.hh
r807 r1230 25 25 // 26 26 // 27 // $Id: G4ScreenedNuclearRecoil.hh,v 1.3 2007/12/07 17:51:10 vnivanch Exp $28 // GEANT4 tag $Name: $27 // G4ScreenedNuclearRecoil.hh,v 1.24 2008/05/01 19:58:59 marcus Exp 28 // GEANT4 tag 29 29 // 30 30 // … … 45 45 // 46 46 // First version, April 2004, Marcus H. Mendenhall, Vanderbilt University 47 // May 1, 2008 -- Added code to allow process to have zero cross section above max energy, to coordinate with G4MSC. -- mhm 47 48 // 48 49 // Class Description - End … … 54 55 #include "globals.hh" 55 56 #include "G4VDiscreteProcess.hh" 57 #include "G4ParticleChange.hh" 56 58 #include "c2_function.hh" 57 59 … … 59 61 #include <vector> 60 62 61 class G4VParticleChange; 63 class G4VNIELPartition; 64 65 typedef c2_const_ptr<G4double> G4_c2_const_ptr; 66 typedef c2_ptr<G4double> G4_c2_ptr; 67 typedef c2_function<G4double> G4_c2_function; 62 68 63 69 typedef struct G4ScreeningTables { 64 70 G4double z1, z2, m1, m2, au, emin; 65 c2_function<G4double> *EMphiData;71 G4_c2_const_ptr EMphiData; 66 72 } G4ScreeningTables; 67 73 … … 70 76 { 71 77 public: 72 G4ScreenedCoulombCrossSectionInfo() { } 73 ~G4ScreenedCoulombCrossSectionInfo() { } 74 75 const char *CVSHeaderVers() { return 76 ""; 77 } 78 79 const char *CVSFileVers() { return ""; } 78 G4ScreenedCoulombCrossSectionInfo() { } 79 ~G4ScreenedCoulombCrossSectionInfo() { } 80 81 static const char* CVSHeaderVers() { return 82 "G4ScreenedNuclearRecoil.hh,v 1.24 2008/05/01 19:58:59 marcus Exp GEANT4 tag "; 83 } 84 static const char* CVSFileVers(); 80 85 }; 81 86 … … 114 119 115 120 // get the mean-free-path table for the indexed material 116 c 2_function<G4double>* operator [] (G4int materialIndex) {117 return MFPTables.find(materialIndex)!=MFPTables.end() ? MFPTables[materialIndex] : (c2_function<G4double>*)0;121 const G4_c2_function * operator [] (G4int materialIndex) { 122 return MFPTables.find(materialIndex)!=MFPTables.end() ? &(MFPTables[materialIndex].get()) : (G4_c2_function *)0; 118 123 } 119 124 … … 122 127 ParticleCache targetMap; 123 128 G4int verbosity; 124 std::map<G4int, c2_function<G4double> *> sigmaMap; // total cross section for each element125 std::map<G4int, c2_function<G4double> *> MFPTables; // MFP for each material129 std::map<G4int, G4_c2_const_ptr > sigmaMap; // total cross section for each element 130 std::map<G4int, G4_c2_const_ptr > MFPTables; // MFP for each material 126 131 127 132 private: … … 134 139 G4ScreenedCoulombCrossSection *crossSection; 135 140 G4double a1, a2, sinTheta, cosTheta, sinZeta, cosZeta, eRecoil; 136 G4ParticleDefinition *recoilIon; } G4CoulombKinematicsInfo; 141 G4ParticleDefinition *recoilIon; 142 const G4Material *targetMaterial; 143 } G4CoulombKinematicsInfo; 137 144 138 145 class G4ScreenedCollisionStage { … … 146 153 147 154 public: 148 G4ScreenedCoulombClassicalKinematics() { }155 G4ScreenedCoulombClassicalKinematics(); 149 156 virtual void DoCollisionStep(class G4ScreenedNuclearRecoil *master, 150 157 const class G4Track& aTrack, const class G4Step& aStep); … … 153 160 const G4ScreeningTables *screen, 154 161 G4double eps, G4double beta); 155 virtual ~G4ScreenedCoulombClassicalKinematics() {} 162 virtual ~G4ScreenedCoulombClassicalKinematics() { } 163 protected: 164 // the c2_functions we need to do the work. 165 c2_const_plugin_function_p<G4double> &phifunc; 166 c2_linear_p<G4double> &xovereps; 167 G4_c2_ptr diff; 168 156 169 }; 157 170 … … 165 178 }; 166 179 180 /** 181 \brief A process which handles screened Coulomb collisions between nuclei 182 183 */ 184 167 185 class G4ScreenedNuclearRecoil : public G4ScreenedCoulombCrossSectionInfo, public G4VDiscreteProcess 168 186 { … … 171 189 friend class G4ScreenedCollisionStage; 172 190 191 /// \brief Construct the process and set some physics parameters for it. 192 /// \param processName the name to assign the process 193 /// \param ScreeningKey the name of a screening function to use. 194 /// The default functions are "zbl" (recommended for soft scattering), 195 /// "lj" (recommended for backscattering) and "mol" (Moliere potential) 196 /// \param GenerateRecoils if frue, ions struck by primary are converted into new moving particles. 197 /// If false, energy is deposited, but no new moving ions are created. 198 /// \param RecoilCutoff energy below which no new moving particles will be created, 199 /// even if \a GenerateRecoils is true. 200 /// Also, a moving primary particle will be stopped if its energy falls below this limit. 201 /// \param PhysicsCutoff the energy transfer to which screening tables are calucalted. 202 /// There is no really 203 /// compelling reason to change it from the 10.0 eV default. However, see the paper on running this 204 /// in thin targets for further discussion, and its interaction with SetMFPScaling() 173 205 G4ScreenedNuclearRecoil(const G4String& processName = "ScreenedElastic", 174 206 const G4String &ScreeningKey="zbl", G4bool GenerateRecoils=1, 175 207 G4double RecoilCutoff=100.0*eV, G4double PhysicsCutoff=10.0*eV); 176 208 /// \brief destructor 177 209 virtual ~G4ScreenedNuclearRecoil(); 178 210 /// \brief used internally by Geant4 machinery 179 211 virtual G4double GetMeanFreePath(const G4Track&, G4double, G4ForceCondition* ); 180 212 /// \brief used internally by Geant4 machinery 181 213 virtual G4VParticleChange* PostStepDoIt(const G4Track& aTrack, const G4Step& aStep); 182 214 /// \brief test if a prticle of type \a aParticleType can use this process 215 /// \param aParticleType the particle to test 183 216 virtual G4bool IsApplicable(const G4ParticleDefinition& aParticleType); 184 217 /// \brief Build physics tables in advance. Not Implemented. 218 /// \param aParticleType the type of particle to build tables for 185 219 virtual void BuildPhysicsTable(const G4ParticleDefinition&) { } 186 220 /// \brief Export physics tables for persistency. Not Implemented. 221 /// \param aParticleType the type of particle to build tables for 187 222 virtual void DumpPhysicsTable(const G4ParticleDefinition& aParticleType); 188 223 /// \brief deterine if the moving particle is within the strong force range of the selected nucleus 224 /// \param A the nucleon number of the beam 225 /// \param A1 the nucleon number of the target 226 /// \param apsis the distance of closest approach 189 227 virtual G4bool CheckNuclearCollision(G4double A, G4double A1, G4double apsis); // return true if hard collision 190 228 191 229 virtual G4ScreenedCoulombCrossSection *GetNewCrossSectionHandler(void); 192 230 193 G4double GetNIEL() const { return NIEL; } // Get non-ionizing energy loss for last step 194 231 /// \brief Get non-ionizing energy loss for last step 232 G4double GetNIEL() const { return NIEL; } 233 234 /// \brief clear precomputed screening tables 195 235 void ResetTables(); // clear all data tables to allow changing energy cutoff, materials, etc. 196 236 237 /// \brief set the upper energy beyond which this process has no cross section 238 /// 239 /// This funciton is used to coordinate this process with G4MSC. Typically, G4MSC should 240 /// not be allowed to operate in a range which overlaps that of this process. The criterion which is most reasonable 241 /// is that the transition should be somewhere in the modestly relativistic regime (500 MeV/u for example). 242 /// \param energy energy per nucleon for the cutoff 243 void SetMaxEnergyForScattering(G4double energy) { processMaxEnergy=energy; } 244 /// \brief find out what screening funciton we are using 197 245 std::string GetScreeningKey() const { return screeningKey; } 246 /// \brief enable or disable all energy deposition by this process 247 /// \param flag if true, enable deposition of energy (the default). If false, disable deposition. 198 248 void AllowEnergyDeposition(G4bool flag) { registerDepositedEnergy=flag; } 249 /// \brief get flag indicating whether deposition is enabled 199 250 G4bool GetAllowEnergyDeposition() const { return registerDepositedEnergy; } 251 /// \brief enable or disable the generation of recoils. 252 /// If recoils are disabled, the energy they would have received is just deposited. 253 /// \param flag if true, create recoil ions in cases in which the energy is above the recoilCutoff. 254 /// If false, just deposit the energy. 200 255 void EnableRecoils(G4bool flag) { generateRecoils=flag; } 256 /// \brief find out if generation of recoils is enabled. 201 257 G4bool GetEnableRecoils() const { return generateRecoils; } 258 /// \brief set the mean free path scaling as specified 259 /// \param scale the factor by which the default MFP will be scaled. 260 /// Set to less than 1 for very thin films, typically, to sample multiple scattering, 261 /// or to greater than 1 for quick simulaitons with a very long flight path. 202 262 void SetMFPScaling(G4double scale) { MFPScale=scale; } 263 /// \brief get the MFPScaling parameter 203 264 G4double GetMFPScaling() const { return MFPScale; } 265 /// \brief enable or disable whether this process will skip collisions 266 /// which are close enough they need hadronic phsyics. Default is true (skip close collisions). 267 /// Disabling this results in excess nuclear stopping power. 268 /// \param flag true results in hard collisions being skipped. false allows hard collisions. 204 269 void AvoidNuclearReactions(G4bool flag) { avoidReactions=flag; } 270 /// \brief get the flag indicating whether hadronic collisions are ignored. 205 271 G4bool GetAvoidNuclearReactions() const { return avoidReactions; } 272 /// \brief set the minimum energy (per nucleon) at which recoils can be generated, 273 /// and the energy (per nucleon) below which all ions are stopped. 274 /// \param energy energy per nucleon 206 275 void SetRecoilCutoff(G4double energy) { recoilCutoff=energy; } 276 /// \brief get the recoil cutoff 207 277 G4double GetRecoilCutoff() const { return recoilCutoff; } 278 /// \brief set the energy to which screening tables are computed. Typically, this is 10 eV or so, and not often changed. 279 /// \param energy the cutoff energy 208 280 void SetPhysicsCutoff(G4double energy) { physicsCutoff=energy; ResetTables(); } 281 /// \brief get the physics cutoff energy. 209 282 G4double GetPhysicsCutoff() const { return physicsCutoff; } 210 class G4ParticleChange &GetParticleChange() { return aParticleChange; } 211 void AddToNIEL(G4double energy) { NIEL+=energy; } 283 /// \brief set the pointer to a class for paritioning energy into NIEL 284 /// \brief part the pointer to the class. 285 void SetNIELPartitionFunction(const G4VNIELPartition *part); 286 /// \brief set the cross section boost to provide faster computation of backscattering 287 /// \param fraction the fraction of particles to have their cross section boosted. 288 /// \param HardeningFactor the factor by which to boost the scattering cross section. 212 289 void SetCrossSectionHardening(G4double fraction, G4double HardeningFactor) { 213 290 hardeningFraction=fraction; 214 291 hardeningFactor=HardeningFactor; 215 292 } 293 /// \brief get the fraction of particles which will have boosted scattering 216 294 G4double GetHardeningFraction() const { return hardeningFraction; } 295 /// \brief get the boost factor in use. 217 296 G4double GetHardeningFactor() const { return hardeningFactor; } 297 /// \brief the the interaciton length used in the last scattering. 218 298 G4double GetCurrentInteractionLength() const { return currentInteractionLength; } 299 /// \brief set a function to compute screening tables, if the user needs non-standard behavior. 300 /// \param cs a class which constructs the screening tables. 219 301 void SetExternalCrossSectionHandler(G4ScreenedCoulombCrossSection *cs) { 220 302 externalCrossSectionConstructor=cs; 221 303 } 304 /// \brief get the verbosity. 222 305 G4int GetVerboseLevel() const { return verboseLevel; } 306 223 307 std::map<G4int, G4ScreenedCoulombCrossSection*> &GetCrossSectionHandlers() 224 308 { return crossSectionHandlers; } … … 228 312 void SetValidCollision(G4bool flag) { validCollision=flag; } 229 313 G4bool GetValidCollision() const { return validCollision; } 314 315 /// \brief get the pointer to our ParticleChange object. for internal use, primarily. 316 class G4ParticleChange &GetParticleChange() { return static_cast<G4ParticleChange &>(*pParticleChange); } 317 /// \brief take the given energy, and use the material information to partition it into NIEL and ionizing energy. 318 void DepositEnergy(G4int z1, G4double a1, const G4Material *material, G4double energy); 230 319 231 320 protected: 232 G4double highEnergyLimit, lowEnergyLimit; 321 /// \brief the energy per nucleon above which the MFP is constant 322 G4double highEnergyLimit; 323 /// \brief the energy per nucleon below which the MFP is zero 324 G4double lowEnergyLimit; 325 /// \brief the energy per nucleon beyond which the cross section is zero, to cross over to G4MSC 326 G4double processMaxEnergy; 233 327 G4String screeningKey; 234 328 G4bool generateRecoils, avoidReactions; 235 329 G4double recoilCutoff, physicsCutoff; 236 330 G4bool registerDepositedEnergy; 237 G4double NIEL;331 G4double IonizingLoss, NIEL; 238 332 G4double MFPScale; 239 333 G4double hardeningFraction, hardeningFactor; … … 243 337 244 338 std::map<G4int, G4ScreenedCoulombCrossSection*> crossSectionHandlers; 245 std::map<G4int, c2_function<G4double>*> meanFreePathTables;246 339 247 340 G4bool validCollision; 248 341 G4CoulombKinematicsInfo kinematics; 249 342 const G4VNIELPartition *NIELPartitionFunction; 250 343 }; 251 344 … … 268 361 std::vector<G4String> GetScreeningKeys() const; 269 362 270 typedef c2_function<G4double>&(*ScreeningFunc)(G4int z1, G4int z2, size_t nPoints, G4double rMax, G4double *au);363 typedef G4_c2_function &(*ScreeningFunc)(G4int z1, G4int z2, size_t nPoints, G4double rMax, G4double *au); 271 364 272 365 void AddScreeningFunction(G4String name, ScreeningFunc fn) { … … 279 372 }; 280 373 281 374 G4_c2_function &ZBLScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double *auval); 375 G4_c2_function &MoliereScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double *auval); 376 G4_c2_function &LJScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double *auval); 377 G4_c2_function &LJZBLScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double *auval); 282 378 283 379 #endif -
trunk/examples/extended/electromagnetic/TestEm7/include/PhysListEmLivermore.hh
r807 r1230 26 26 // 27 27 // $Id: PhysListEmLivermore.hh,v 1.1 2006/11/22 18:56:20 vnivanch Exp $ 28 // GEANT4 tag $Name: $28 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 29 29 // 30 30 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/PhysListEmPenelope.hh
r807 r1230 26 26 // 27 27 // $Id: PhysListEmPenelope.hh,v 1.1 2006/11/22 18:56:21 vnivanch Exp $ 28 // GEANT4 tag $Name: $28 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 29 29 // 30 30 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/PhysListEmStandard.hh
r807 r1230 26 26 // 27 27 // $Id: PhysListEmStandard.hh,v 1.3 2006/06/29 16:57:44 gunter Exp $ 28 // GEANT4 tag $Name: $28 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 29 29 // 30 30 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/PhysListEmStandardNR.hh
r807 r1230 25 25 // 26 26 // $Id: PhysListEmStandardNR.hh,v 1.1 2008/01/14 12:11:38 vnivanch Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/PhysListEmStandardSS.hh
r807 r1230 25 25 // 26 26 // $Id: PhysListEmStandardSS.hh,v 1.1 2006/10/24 11:37:56 vnivanch Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/PhysicsList.hh
r807 r1230 25 25 // 26 26 // 27 // $Id: PhysicsList.hh,v 1. 6 2006/11/22 18:56:21vnivanch Exp $28 // GEANT4 tag $Name: $27 // $Id: PhysicsList.hh,v 1.8 2008/11/20 20:34:50 vnivanch Exp $ 28 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 29 29 // 30 30 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... … … 39 39 40 40 #include "G4VModularPhysicsList.hh" 41 #include "G4EmConfigurator.hh" 41 42 #include "globals.hh" 42 43 … … 50 51 { 51 52 public: 53 52 54 PhysicsList(); 53 55 virtual ~PhysicsList(); … … 67 69 68 70 private: 71 72 G4EmConfigurator em_config; 73 69 74 G4double cutForGamma; 70 75 G4double cutForElectron; … … 77 82 G4String emName; 78 83 G4VPhysicsConstructor* emPhysicsList; 84 G4VPhysicsConstructor* decPhysicsList; 79 85 std::vector<G4VPhysicsConstructor*> hadronPhys; 80 86 -
trunk/examples/extended/electromagnetic/TestEm7/include/PhysicsListMessenger.hh
r807 r1230 25 25 // 26 26 // $Id: PhysicsListMessenger.hh,v 1.3 2006/06/29 16:57:52 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/PrimaryGeneratorAction.hh
r807 r1230 25 25 // 26 26 // $Id: PrimaryGeneratorAction.hh,v 1.3 2006/06/29 16:57:54 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/PrimaryGeneratorMessenger.hh
r807 r1230 25 25 // 26 26 // $Id: PrimaryGeneratorMessenger.hh,v 1.3 2006/06/29 16:57:56 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/RunAction.hh
r807 r1230 24 24 // ******************************************************************** 25 25 // 26 // $Id: RunAction.hh,v 1.1 2 2008/01/14 12:11:39vnivanch Exp $27 // GEANT4 tag $Name: geant4-09-0 1-patch-02$26 // $Id: RunAction.hh,v 1.14 2008/08/22 18:30:27 vnivanch Exp $ 27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... … … 40 40 class PrimaryGeneratorAction; 41 41 class G4Run; 42 43 namespace AIDA { 44 class IAnalysisFactory; 45 class ITree; 46 class IHistogram1D; 47 } 42 class Histo; 48 43 49 44 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... … … 52 47 { 53 48 public: 54 RunAction(DetectorConstruction*, PhysicsList*,PrimaryGeneratorAction*); 49 RunAction(DetectorConstruction*, PhysicsList*, 50 PrimaryGeneratorAction*); 55 51 virtual ~RunAction(); 56 52 … … 58 54 void EndOfRunAction(const G4Run*); 59 55 60 void FillTallyEdep(G4int n, G4double e) {tallyEdep[n] += e;};56 void FillTallyEdep(G4int n, G4double e) {tallyEdep[n] += e;}; 61 57 void FillEdep(G4double de, G4double eni) {edeptot += de; eniel += eni;}; 62 58 63 59 G4double GetBinLength() {return binLength;}; 64 60 G4double GetLength() {return length;}; 65 G4double GetOffsetX() {return offsetX;} 66 void FillHisto(G4int id, G4double x, G4double weight = 1.0); 61 G4double GetOffsetX() {return offsetX;} 62 63 void FillHisto(G4int id, G4double x, G4double weight = 1.0); 67 64 68 void AddProjRange (G4double x) {projRange += x; projRange2 += x*x;}; 65 void AddProjRange (G4double x) 66 {projRange += x; projRange2 += x*x; nRange++;}; 69 67 void AddPrimaryStep() {nPrimarySteps++;}; 70 68 71 69 private: 72 void bookHisto();73 void cleanHisto();74 70 75 private:76 71 DetectorConstruction* detector; 77 72 PhysicsList* physics; … … 84 79 G4double edeptot, eniel; 85 80 G4int nPrimarySteps; 86 87 AIDA::IAnalysisFactory* af; 88 AIDA::ITree* tree; 89 AIDA::IHistogram1D* histo[1]; 81 G4int nRange; 82 83 Histo* histo; 90 84 }; 91 85 -
trunk/examples/extended/electromagnetic/TestEm7/include/StepMax.hh
r807 r1230 25 25 // 26 26 // $Id: StepMax.hh,v 1.3 2006/06/29 16:58:01 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/StepMaxMessenger.hh
r807 r1230 25 25 // 26 26 // $Id: StepMaxMessenger.hh,v 1.2 2006/06/29 16:58:03 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/SteppingAction.hh
r807 r1230 25 25 // 26 26 // $Id: SteppingAction.hh,v 1.2 2006/06/29 16:58:05 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/SteppingVerbose.hh
r807 r1230 25 25 // 26 26 // $Id: SteppingVerbose.hh,v 1.2 2006/06/29 16:58:07 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/TrackingAction.hh
r807 r1230 25 25 // 26 26 // $Id: TrackingAction.hh,v 1.2 2006/06/29 16:58:09 gunter Exp $ 27 // GEANT4 tag $Name: $27 // GEANT4 tag $Name: geant4-09-03-cand-01 $ 28 28 // 29 29 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -
trunk/examples/extended/electromagnetic/TestEm7/include/c2_function.hh
r807 r1230 1 // 2 // ******************************************************************** 3 // * License and Disclaimer * 4 // * * 5 // * The Geant4 software is copyright of the Copyright Holders of * 6 // * the Geant4 Collaboration. It is provided under the terms and * 7 // * conditions of the Geant4 Software License, included in the file * 8 // * LICENSE and available at http://cern.ch/geant4/license . These * 9 // * include a list of copyright holders. * 10 // * * 11 // * Neither the authors of this software system, nor their employing * 12 // * institutes,nor the agencies providing financial support for this * 13 // * work make any representation or warranty, express or implied, * 14 // * regarding this software system or assume any liability for its * 15 // * use. Please see the license in the file LICENSE and URL above * 16 // * for the full disclaimer and the limitation of liability. * 17 // * * 18 // * This code implementation is the result of the scientific and * 19 // * technical work of the GEANT4 collaboration. * 20 // * By using, copying, modifying or distributing the software (or * 21 // * any work based on the software) you agree to acknowledge its * 22 // * use in resulting scientific publications, and indicate your * 23 // * acceptance of all terms of the Geant4 Software license. * 24 // ******************************************************************** 25 // 26 // 1 27 /** 2 28 * \file … … 7 33 * \author Copyright 2005 __Vanderbilt University__. All rights reserved. 8 34 * 9 * \version c2_function.hh,v 1.53 2007/11/12 13:58:57 marcus Exp 35 * \version c2_function.hh,v 1.238 2008/05/22 12:45:19 marcus Exp 36 * \see \ref c2_factory "Factory Functions" for information on constructing things in here 10 37 */ 11 38 12 #ifndef __has_C2Functions_c2_h 13 #define __has_C2Functions_c2_h 1 39 #ifndef __has_c2_function_hh 40 #define __has_c2_function_hh 1 41 42 // MSVC does not automatically define numerical constants such as M_PI without this. 43 // this came from the msdn website, so it should be right... 44 #ifdef _MSC_VER 45 #define _USE_MATH_DEFINES 46 #define c2_isnan _isnan 47 #define c2_isfinite _finite 48 #else 49 #define c2_isnan std::isnan 50 #define c2_isfinite std::isfinite 51 #endif 14 52 15 53 #include <cmath> 16 54 #include <vector> 55 #include <utility> 17 56 #include <string> 57 #include <stdexcept> 58 #include <typeinfo> 59 #include <sstream> 18 60 19 61 /// \brief the exception class for c2_function operations. … … 32 74 33 75 // put these forward references here, and with a bogus typename to make swig happy. 34 template <typename float_type> class c2_composed_function; 35 template <typename float_type> class c2_sum; 36 template <typename float_type> class c2_diff; 37 template <typename float_type> class c2_product; 38 template <typename float_type> class c2_ratio; 76 template <typename float_type> class c2_composed_function_p; 77 template <typename float_type> class c2_sum_p; 78 template <typename float_type> class c2_diff_p; 79 template <typename float_type> class c2_product_p; 80 template <typename float_type> class c2_ratio_p; 81 template <typename float_type> class c2_piecewise_function_p; 82 template <typename float_type> class c2_quadratic_p; 83 template <typename float_type> class c2_ptr; 84 /** 85 \defgroup abstract_classes Abstract Classes 86 \defgroup arithmetic_functions Arithmetic Functions 87 \defgroup math_functions Mathemetical Functions 88 \defgroup parametric_functions Parametric Families of Functions 89 \defgroup interpolators Interpolating Functions 90 \defgroup containers Functions which are containers for, or functions of, other functions 91 \defgroup factories Factory classes which reduce silly template typing 92 \defgroup transforms Classes which provide coordinate system transformations, wih derivatives 93 */ 94 95 /// \brief structure used to hold evaluated function data at a point. 96 /// 97 /// Contains all the information for the function at one point. 98 template <typename float_type> class c2_fblock 99 { 100 public: 101 /// \brief the abscissa 102 float_type x; 103 /// \brief the value of the function at \a x 104 float_type y; 105 /// \brief the derivative at \a x 106 float_type yp; 107 /// \brief the second derivative at \a x 108 float_type ypp; 109 /// flag, filled in by c2_function::fill_fblock(), indicating the derivative is NaN of Inf 110 bool ypbad; 111 /// flag, filled in by c2_function::fill_fblock(), indicating the second derivative is NaN of Inf 112 bool yppbad; 113 }; 39 114 40 115 /** 41 116 \brief the parent class for all c2_functions. 42 117 \ingroup abstract_classes 43 118 c2_functions know their value, first, and second derivative at almost every point. 44 119 They can be efficiently combined with binary operators, via c2_binary_function, 45 composed via c2_composed_function ,120 composed via c2_composed_function_, 46 121 have their roots found via find_root(), 47 122 and be adaptively integrated via partial_integrals() or integral(). … … 53 128 log_log_interpolating_function, and arrhenius_interpolating_function, 54 129 as well as the template functions 55 inverse_integrated_density ().130 inverse_integrated_density_function(). 56 131 57 \warning 58 The composite flavors of c2_functions (c2_sum, c2_composed_function, c2_binary_function, e.g.) make no effort to manage 59 deletion of their component functions. 60 These are just container classes, and the user (along with normal automatic variable semantics) 61 is responsible for the lifetime of components. 62 Inappropriate attention to this can cause massive memory leaks. 63 However, in most cases these do exactly what is intended. 64 The classes will be left this way since the only other option is to use copy constructors on everything, 65 which would make this all very slow. 132 For a discussion of memory management, see \ref memory_management 66 133 67 134 */ … … 71 138 /// \return the CVS Id string 72 139 const std::string cvs_header_vers() const { return 73 "c2_function.hh,v 1. 53 2007/11/12 13:58:57marcus Exp";140 "c2_function.hh,v 1.238 2008/05/22 12:45:19 marcus Exp"; 74 141 } 75 142 … … 80 147 public: 81 148 /// \brief destructor 82 virtual ~c2_function() { if(sampling_grid && !no_overwrite_grid) delete sampling_grid; } 149 virtual ~c2_function() { 150 if(sampling_grid && !no_overwrite_grid) delete sampling_grid; 151 if(root_info) delete root_info; 152 if(owner_count) { 153 std::ostringstream outstr; 154 outstr << "attempt to delete an object with non-zero ownership in class "; 155 outstr << typeid(*this).name() << std::endl; 156 throw c2_exception(outstr.str().c_str()); 157 } 158 } 83 159 84 160 /// \brief get the value and derivatives. … … 97 173 inline float_type operator () (float_type x) const throw(c2_exception) 98 174 { return value_with_derivatives(x, (float_type *)0, (float_type *)0); } 99 100 /// \brief compose this function outside another.101 /// \param inner the inner function102 /// \return the composed function103 c2_composed_function<float_type> & operator ()(const c2_function<float_type> &inner) const104 { return *new c2_composed_function<float_type>((*this), inner); }105 175 106 176 /// \brief get the value and derivatives. … … 130 200 /// \param[out] final_yprime2 If pointer is not zero, return second derivative of function at root 131 201 /// \return the position of the root. 202 /// \see \ref rootfinder_subsec "Root finding sample" 132 203 float_type find_root(float_type lower_bracket, float_type upper_bracket, float_type start, 133 204 float_type value, int *error=0, … … 151 222 /// with no error checking. 152 223 /// \param extrapolate if true, use simple Richardson extrapolation on the final 2 steps to reduce the error. 153 /// \return sum of partial integrals, wh cih is the definite integral from the first value in \a xgrid to the last.224 /// \return sum of partial integrals, which is the definite integral from the first value in \a xgrid to the last. 154 225 float_type partial_integrals(std::vector<float_type> xgrid, std::vector<float_type> *partials = 0, 155 float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) const; 226 float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) 227 const throw(c2_exception); 156 228 157 229 /// \brief a fully-automated integrator which uses the information provided by the get_sampling_grid() function … … 164 236 /// \param xmax upper bound of the domain for integration 165 237 /// \param partials if non-NULL, a vector in which to receive the partial integrals. 166 /// It will automatically be sized appr propriately, if provided, to contain \a n - 1 elements where \a n is the length of \a xgrid238 /// It will automatically be sized appropriately, if provided, to contain \a n - 1 elements where \a n is the length of \a xgrid 167 239 /// \param abs_tol the absolute error bound for each segment 168 240 /// \param rel_tol the fractional error bound for each segment. … … 173 245 /// with no error checking. 174 246 /// \param extrapolate if true, use simple Richardson extrapolation on the final 2 steps to reduce the error. 175 /// \return sum of partial integrals, wh cih is the definite integral from the first value in \a xgrid to the last.247 /// \return sum of partial integrals, which is the definite integral from the first value in \a xgrid to the last. 176 248 float_type integral(float_type xmin, float_type xmax, std::vector<float_type> *partials = 0, 177 float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) const; 178 249 float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) 250 const throw(c2_exception); 251 252 /// \brief create a c2_piecewise_function_p from c2_connector_function_p segments which 253 /// is a representation of the parent function to the specified accuracy, but maybe much cheaper to evaluate 254 /// 255 /// This method has three modes, depending on the \a derivs flag. 256 /// 257 /// If \a derivs is 2, 258 /// it computes a c2_piecewise_function_p representation of its parent function, which may be a much faster 259 /// function to use in codes if the parent function is expensive. If \a xvals and \a yvals are non-null, 260 /// it will also fill them in with the function values at each grid point the adaptive algorithm chooses. 261 /// 262 /// If \a derivs is 1, this does not create the connectors, 263 /// and returns an null pointer, but will fill in the \a xvals and \a yvals 264 /// vectors with values of the function at points such that the linear interpolation error between the points 265 /// is bounded by the tolerance values given. Because it uses derivative information from the function to manage the 266 /// error control, it is almost completely free of issues with missing periods of oscillatory functions, 267 /// even with no information provided in the sampling grid. 268 /// This is typically useful for sampling a function for plotting. 269 /// 270 /// If \a derivs is 0, this does something very like what it does if \a derivs = 1, but without derivatives. 271 /// Instead, to compute the intermediate value of the function for error control, it just uses 272 /// 3-point parabolic interpolation. This is useful amost exclusively for converting a non-c2_function, 273 /// with no derivatives, but wrapped in a c2_classic_function wrapper, into a table of values to seed an interpolating_function_p. 274 /// Note, however, that without derivatives, this is very susceptible to missing periods of oscillatory 275 /// functions, so it is important to set a sampling grid which isn't too much coarser than the typical oscillations. 276 /// 277 /// \note the \a sampling_grid of the returned function matches the \a sampling_grid of its parent. 278 /// \see \ref sample_function_for_plotting "Adaptive Sampling Examples" 279 /// \param xmin lower bound of the domain for sampling 280 /// \param xmax upper bound of the domain for sampling 281 /// \param abs_tol the absolute error bound for each segment 282 /// \param rel_tol the fractional error bound for each segment. 283 /// \param derivs if 0 or 1, return a useless function, but fill in the \a xvals and \a yvals vectors (if non-null). 284 /// Also, if 0 or 1, tolerances refer to linear interpolation, not high-order interpolation. 285 /// If 2, return a full piecewise collection of c2_connector_function_p segments. See discussion above. 286 /// \param [in,out] xvals vector of abscissas at which the function was actually sampled (if non-null) 287 /// \param [in,out] yvals vector of function values corresponding to \a xvals (if non-null) 288 /// \return a new, sampled representation, if \a derivs is 2. A null pointer if \a derivs is 0 or 1. 289 c2_piecewise_function_p<float_type> *adaptively_sample(float_type xmin, float_type xmax, 290 float_type abs_tol=1e-12, float_type rel_tol=1e-12, 291 int derivs=2, std::vector<float_type> *xvals=0, std::vector<float_type> *yvals=0) const throw(c2_exception); 292 179 293 /// \brief return the lower bound of the domain for this function as set by set_domain() 180 294 inline float_type xmin() const { return fXMin; } … … 186 300 /// \brief this is a counter owned by the function but which can be used to monitor efficiency of algorithms. 187 301 /// 188 /// It is not maintained automatically in general! 189 /// The adaptive integrator and root finder do clear it at the start and update it for performance checking. 302 /// It is not maintained automatically in general! The root finder, integrator, and sampler do increment it. 190 303 /// \return number of evaluations logged since last reset. 191 volatile int get_evaluations() const { return evaluations; }304 volatile size_t get_evaluations() const { return evaluations; } 192 305 /// \brief reset the counter 193 306 void reset_evaluations() const { evaluations=0; } // evaluations are 'invisible' to constant … … 200 313 /// \param message an informative string to include in an exception if this throws c2_exception 201 314 /// \return true if in decreasing order, false if increasing 202 bool check_monotonicity(const std::vector<float_type> &data, const char message[]) throw(c2_exception);315 bool check_monotonicity(const std::vector<float_type> &data, const char message[]) const throw(c2_exception); 203 316 204 317 /// \brief establish a grid of 'interesting' points on the function. … … 212 325 virtual void set_sampling_grid(const std::vector<float_type> &grid) throw(c2_exception); 213 326 327 /// \brief get the sampling grid, which may be a null pointer 328 /// \return pointer to the sampling grid 329 std::vector<float_type> *get_sampling_grid_pointer() const { return sampling_grid; } 330 214 331 /// \brief return the grid of 'interesting' points along this function which lie in the region requested 215 332 /// … … 217 334 /// \param xmin the lower bound for which the function is to be sampled 218 335 /// \param xmax the upper bound for which the function is to be sampled 219 /// \ return a newvector containing the samplng grid.220 virtual std::vector<float_type> &get_sampling_grid(float_type xmin, float_type xmax) const ;336 /// \param [in,out] grid filled vector containing the samplng grid. 337 virtual void get_sampling_grid(float_type xmin, float_type xmax, std::vector<float_type> &grid) const ; 221 338 222 339 /// \brief clean up endpoints on a grid of points 223 ///224 340 /// \param[in,out] result the sampling grid with excessively closely space endpoints removed. 225 341 /// The grid is modified in place. 226 342 void preen_sampling_grid(std::vector<float_type> *result) const; 227 343 /// \brief refine a grid by splitting each interval into more intervals 228 /// \param grid the grid to refine344 /// \param [in,out] grid the grid to refine in place 229 345 /// \param refinement the number of new steps for each old step 230 /// \return a new sampling grid with more points. 231 std::vector<float_type> & refine_sampling_grid(const std::vector<float_type> &grid, size_t refinement) const; 346 void refine_sampling_grid(std::vector<float_type> &grid, size_t refinement) const; 232 347 233 348 /// \brief create a new c2_function from this one which is normalized on the interval … … 236 351 /// \param norm the desired integral for the function over the region 237 352 /// \return a new c2_function with the desired \a norm. 238 c2_function<float_type> &normalized_function(float_type xmin, float_type xmax, float_type norm=1.0) ;353 c2_function<float_type> &normalized_function(float_type xmin, float_type xmax, float_type norm=1.0) const throw(c2_exception); 239 354 /// \brief create a new c2_function from this one which is square-normalized on the interval 240 355 /// \param xmin lower bound of the domain for integration … … 242 357 /// \param norm the desired integral for the function over the region 243 358 /// \return a new c2_function with the desired \a norm. 244 c2_function<float_type> &square_normalized_function(float_type xmin, float_type xmax, float_type norm=1.0) ;359 c2_function<float_type> &square_normalized_function(float_type xmin, float_type xmax, float_type norm=1.0) const throw(c2_exception); 245 360 /// \brief create a new c2_function from this one which is square-normalized with the provided \a weight on the interval 246 361 /// \param xmin lower bound of the domain for integration … … 249 364 /// \param norm the desired integral for the function over the region 250 365 /// \return a new c2_function with the desired \a norm. 251 c2_function<float_type> &square_normalized_function(float_type xmin, float_type xmax, const c2_function<float_type> &weight, float_type norm=1.0); 252 253 /// \brief factory function to create a c2_sum from an regular algebraic expression. 254 /// \note 255 /// be very wary of ownership issues if this is used in a complex expression. 256 c2_sum<float_type> &operator + (const c2_function<float_type> &rhs) { return *new c2_sum<float_type>(*this, rhs); } 257 /// \brief factory function to create a c2_diff from an regular algebraic expression. 258 /// \note 259 /// be very wary of ownership issues if this is used in a complex expression. 260 c2_diff<float_type> &operator - (const c2_function<float_type> &rhs) { return *new c2_diff<float_type>(*this, rhs); } 261 /// \brief factory function to create a c2_product from an regular algebraic expression. 262 /// \note 263 /// be very wary of ownership issues if this is used in a complex expression. 264 c2_product<float_type> &operator * (const c2_function<float_type> &rhs) { return *new c2_product<float_type>(*this, rhs); } 265 /// \brief factory function to create a c2_ratio from an regular algebraic expression. 266 /// \note 267 /// be very wary of ownership issues if this is used in a complex expression. 268 c2_ratio<float_type> &operator / (const c2_function<float_type> &rhs) { return *new c2_ratio<float_type>(*this, rhs); } 269 270 271 272 std::vector<float_type> *sampling_grid; 273 bool no_overwrite_grid; 274 366 c2_function<float_type> &square_normalized_function( 367 float_type xmin, float_type xmax, const c2_function<float_type> &weight, float_type norm=1.0) 368 const throw(c2_exception); 369 370 /// \brief factory function to create a c2_sum_p from a regular algebraic expression. 371 /// \param rhs the right-hand term of the sum 372 /// \return a new c2_function 373 c2_sum_p<float_type> &operator + (const c2_function<float_type> &rhs) const 374 { return *new c2_sum_p<float_type>(*this, rhs); } 375 /// \brief factory function to create a c2_diff_p from a regular algebraic expression. 376 /// \param rhs the right-hand term of the difference 377 /// \return a new c2_function 378 c2_diff_p<float_type> &operator - (const c2_function<float_type> &rhs) const 379 { return *new c2_diff_p<float_type>(*this, rhs); } 380 /// \brief factory function to create a c2_product_p from a regular algebraic expression. 381 /// \param rhs the right-hand term of the product 382 /// \return a new c2_function 383 c2_product_p<float_type> &operator * (const c2_function<float_type> &rhs) const 384 { return *new c2_product_p<float_type>(*this, rhs); } 385 /// \brief factory function to create a c2_ratio_p from a regular algebraic expression. 386 /// \param rhs the right-hand term of the ratio (the denominator) 387 /// \return a new c2_function 388 c2_ratio_p<float_type> &operator / (const c2_function<float_type> &rhs) const 389 { return *new c2_ratio_p<float_type>(*this, rhs); } 390 /// \brief compose this function outside another. 391 /// \param inner the inner function 392 /// \return the composed function 393 /// \anchor compose_operator 394 c2_composed_function_p<float_type> & operator ()(const c2_function<float_type> &inner) const 395 { return *new c2_composed_function_p<float_type>((*this), inner); } 396 397 /// \brief Find out where a calculation ran into trouble, if it got a nan. 398 /// If the most recent computation did not return a nan, this is undefined. 399 /// \return \a x value of point at which something went wrong, if integrator (or otherwise) returned a nan. 400 float_type get_trouble_point() const { return bad_x_point; } 401 402 /// \brief increment our reference count. Destruction is only legal if the count is zero. 403 void claim_ownership() const { owner_count++; } 404 /// \brief decrement our reference count. Do not destroy at zero. 405 /// \return final owner count, to check whether object should disappear. 406 size_t release_ownership_for_return() const throw(c2_exception) { 407 if(!owner_count) { 408 std::ostringstream outstr; 409 outstr << "attempt to release ownership of an unowned function in class "; 410 outstr << typeid(*this).name() << std::endl; 411 throw c2_exception(outstr.str().c_str()); 412 } 413 owner_count--; 414 return owner_count; 415 } 416 /// \brief decrement our reference count. If the count reaches zero, destroy ourself. 417 void release_ownership() const throw(c2_exception) { 418 if(!release_ownership_for_return()) delete this; 419 } 420 /// \brief get the reference count, mostly for debugging 421 /// \return the count 422 size_t count_owners() const { return owner_count; } 423 275 424 protected: 276 425 c2_function(const c2_function<float_type> &src) : sampling_grid(0), 277 426 no_overwrite_grid(false), 278 fXMin(src.fXMin), fXMax(src.fXMax), root Initialized(false)427 fXMin(src.fXMin), fXMax(src.fXMax), root_info(0), owner_count(0) 279 428 {} // copy constructor only copies domain, and is only for internal use 280 429 c2_function() : 281 430 sampling_grid(0), no_overwrite_grid(0), 282 431 fXMin(-std::numeric_limits<float_type>::max()), 283 fXMax(std::numeric_limits<float_type>::max()), 284 rootInitialized(false) 432 fXMax(std::numeric_limits<float_type>::max()), root_info(0), owner_count(0) 285 433 {} // prevent accidental naked construction (impossible any since this has pure virtual methods) 286 434 … … 293 441 } 294 442 443 std::vector<float_type> * sampling_grid; 444 bool no_overwrite_grid; 445 295 446 float_type fXMin, fXMax; 296 mutable int evaluations; 297 447 mutable size_t evaluations; 448 /// \brief this point may be used to record where a calculation ran into trouble 449 mutable float_type bad_x_point; 450 public: 451 /// \brief fill in a c2_fblock<float_type>... a shortcut for the integrator & sampler 452 /// \param [in,out] fb the block to fill in with information 453 inline void fill_fblock(c2_fblock<float_type> &fb) const throw(c2_exception) 454 { 455 fb.y=value_with_derivatives(fb.x, &fb.yp, &fb.ypp); 456 fb.ypbad=c2_isnan(fb.yp) || !c2_isfinite(fb.yp); 457 fb.yppbad=c2_isnan(fb.ypp) || !c2_isfinite(fb.ypp); 458 increment_evaluations(); 459 } 460 298 461 private: 299 /// \brief structure used for recursion in adaptive integrator. 300 /// 301 /// Contains all the information for the function at one point. 302 struct c2_integrate_fblock { float_type x, y, yp, ypp; }; 303 /// \brief structure used to pass information recursively. 462 /// \brief the data element for the internal recursion stack for the sampler and integrator 463 struct recur_item { 464 c2_fblock<float_type> f1; size_t depth; 465 float_type previous_estimate, abs_tol, step_sum; 466 bool done; 467 size_t f0index, f2index; 468 }; 469 470 471 /// \brief structure used to pass information recursively in integrator. 304 472 /// 305 473 /// the \a abs_tol is scaled by a factor of two at each division. 306 474 /// Everything else is just passed down. 307 struct c2_integrate_recur { struct c2_integrate_fblock *f0, *f1, *f2; 308 float_type abs_tol, rel_tol, *lr, eps_scale, extrap_coef, extrap2; 309 int depth, derivs; 310 bool adapt, extrapolate; 475 struct c2_integrate_recur { 476 c2_fblock<float_type> *f0, *f1; 477 float_type abs_tol, rel_tol, eps_scale, extrap_coef, extrap2, dx_tolerance, abs_tol_min; 478 std::vector< recur_item > *rb_stack; 479 int derivs; 480 bool adapt, extrapolate, inited; 481 }; 482 483 /// \brief structure used to pass information recursively in sampler. 484 /// 485 struct c2_sample_recur { 486 c2_fblock<float_type> *f0, *f1; 487 float_type abs_tol, rel_tol, dx_tolerance, abs_tol_min; 488 int derivs; 489 c2_piecewise_function_p<float_type> *out; 490 std::vector<float_type> *xvals, *yvals; 491 std::vector< recur_item > *rb_stack; 492 bool inited; 493 }; 494 495 /// \brief structure used to hold root bracketing information 496 /// 497 struct c2_root_info { 498 c2_fblock<float_type> lower, upper; 499 bool inited; 311 500 }; 312 501 … … 316 505 /// to allow very efficient recursion. 317 506 /// \param rb a pointer to the recur struct. 318 float_type integrate_step(struct c2_integrate_recur &rb) const ;507 float_type integrate_step(struct c2_integrate_recur &rb) const throw(c2_exception); 319 508 320 /// these carry a memory of the last root bracketing, 509 /// \brief Carry out the recursive subdivision for sampling. 510 /// 511 /// This passes information recursively through the \a recur block pointer 512 /// to allow very efficient recursion. 513 /// \param rb a pointer to the recur struct. 514 void sample_step(struct c2_sample_recur &rb) const throw(c2_exception); 515 516 /// this carry a memory of the last root bracketing, 321 517 /// to avoid the necessity of evaluating the function on the brackets every time 322 518 /// if the brackets have not been changed. 323 mutable float_type lastRootLowerX, lastRootUpperX, lastRootLowerY, lastRootUpperY; 324 mutable int rootInitialized; 325 326 }; 327 519 /// it is declared as a pointer, since many c2_functions may never need one allocated 520 mutable struct c2_root_info *root_info; 521 522 mutable size_t owner_count; 523 }; 524 525 /// \brief a container into which any conventional c-style function can be dropped, 526 /// to create a degenerate c2_function without derivatives. 527 /// Mostly useful for sampling into interpolating functions. 528 /// construct a reference to this with c2_classic_function() 529 /// \ingroup containers 530 /// The factory function c2_factory::classic_function() creates *new c2_classic_function_p() 531 template <typename float_type=double> class c2_classic_function_p : public c2_function<float_type> { 532 public: 533 /// \brief construct the container 534 /// \param c_func a pointer to a conventional c-style function 535 c2_classic_function_p(const float_type (*c_func)(float_type)) : c2_function<float_type>(), func(c_func) {} 536 537 /// \copydoc c2_function::value_with_derivatives 538 /// Uses the internal function pointer set by set_function(). 539 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 540 { 541 if(!func) throw c2_exception("c2_classic_function called with null function"); 542 if(yprime) *yprime=0; 543 if(yprime2) *yprime2=0; 544 return func(x); 545 } 546 ~c2_classic_function_p() { } 547 548 protected: 549 /// \brief pointer to our function 550 const float_type (*func)(float_type); 551 }; 552 553 /// \brief create a container for a c2_function which handles the reference counting. 554 /// \ingroup containers 555 /// It is useful as a smart container to hold a c2_function and keep the reference count correct. 556 /// The recommended way for a class to store a c2_function which is handed in from the outside 557 /// is for it to have a c2_ptr member into which the passed-in function is stored. 558 /// This way, when the class instance is deleted, it will automatically dereference any function 559 /// which it was handed. 560 /// 561 /// This class contains a copy constructor and operator=, to make it fairly easy to make 562 /// a std::vector of these objects, and have it work as expected. 563 template <typename float_type> class c2_const_ptr { 564 public: 565 /// \brief construct the container with no function 566 c2_const_ptr() : func(0) {} 567 /// \brief construct the container with a pre-defined function 568 /// \param f the function to store 569 c2_const_ptr(const c2_function<float_type> &f) : func(0) 570 { set_function(&f); } 571 /// \brief copy constructor 572 /// \param src the container to copy 573 c2_const_ptr(const c2_const_ptr<float_type> &src) : func(0) 574 { set_function(src.get_ptr()); } 575 /// \brief fill the container with a new function, or clear it with a null pointer 576 /// \param f the function to store, releasing any previously held function 577 void set_function(const c2_function<float_type> *f) 578 { 579 if(func) func->release_ownership(); 580 func=f; 581 if(func) func->claim_ownership(); 582 } 583 584 /// \brief fill the container from another container 585 /// \param f the container to copy 586 void operator =(const c2_const_ptr<float_type> &f) 587 { set_function(f.get_ptr()); } 588 /// \brief fill the container with a function 589 /// \param f the function 590 void operator =(const c2_function<float_type> &f) 591 { set_function(&f); } 592 /// \brief release the function without destroying it, so it can be returned from a function 593 /// 594 /// This is usually the very last line of a function before the return statement, so that 595 /// any exceptions that happen during execution of the function will cause proper cleanup. 596 /// Once the function has been released from its container this way, it is an orhpaned object 597 /// until the caller claims it, so it could get lost if an exception happens. 598 void release_for_return() throw(c2_exception) 599 { 600 if(func) func->release_ownership_for_return(); 601 func=0; 602 } 603 /// \brief clear the function 604 /// 605 /// Any attempt to use this c2_plugin_function_p throws an exception if the saved function is cleared. 606 void unset_function(void) { set_function(0); } 607 /// \brief destructor 608 ~c2_const_ptr() { set_function(0); } 609 610 /// \brief get a reference to our owned function 611 inline const c2_function<float_type> &get() const throw(c2_exception) 612 { 613 if(!func) throw c2_exception("c2_ptr accessed uninitialized"); 614 return *func; 615 } 616 /// \brief get an unchecked pointer to our owned function 617 inline const c2_function<float_type> *get_ptr() const { return func; } 618 /// \brief get a checked pointer to our owned function 619 inline const c2_function<float_type> *operator -> () const 620 { return &get(); } 621 /// \brief check if we have a valid function 622 bool valid() const { return func != 0; } 623 624 /// \brief type coercion operator which lets us use a pointer as if it were a const c2_function 625 operator const c2_function<float_type>& () const { return this->get(); } 626 627 /// \brief convenience operator to make us look like a function 628 /// \param x the value at which to evaluate the contained function 629 /// \return the evaluated function 630 /// \note If you using this repeatedly, do const c2_function<float_type> &func=ptr; 631 /// and use func(x). Calling this operator wastes some time, since it checks the validity of the 632 /// pointer every time. 633 float_type operator()(float_type x) const throw(c2_exception) { return get()(x); } 634 /// \brief convenience operator to make us look like a function 635 /// \param x the value at which to evaluate the contained function 636 /// \param yprime the derivative 637 /// \param yprime2 the second derivative 638 /// \return the evaluated function 639 /// \note If you using this repeatedly, do const c2_function<float_type> &func=ptr; 640 /// and use func(x). Calling this operator wastes some time, since it checks the validity of the 641 /// pointer every time. 642 float_type operator()(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 643 { return get().value_with_derivatives(x, yprime, yprime2); } 644 /// \brief factory function to create a c2_sum_p from a regular algebraic expression. 645 /// \param rhs the right-hand term of the sum 646 /// \return a new c2_function 647 c2_sum_p<float_type> &operator + (const c2_function<float_type> &rhs) const throw(c2_exception) 648 { return *new c2_sum_p<float_type>(get(), rhs); } 649 /// \brief factory function to create a c2_diff_p from a regular algebraic expression. 650 /// \param rhs the right-hand term of the difference 651 /// \return a new c2_function 652 c2_diff_p<float_type> &operator - (const c2_function<float_type> &rhs) const throw(c2_exception) 653 { return *new c2_diff_p<float_type>(get(), rhs); } 654 /// \brief factory function to create a c2_product_p from a regular algebraic expression. 655 /// \param rhs the right-hand term of the product 656 /// \return a new c2_function 657 c2_product_p<float_type> &operator * (const c2_function<float_type> &rhs) const throw(c2_exception) 658 { return *new c2_product_p<float_type>(get(), rhs); } 659 /// \brief factory function to create a c2_ratio_p from a regular algebraic expression. 660 /// \param rhs the right-hand term of the ratio (the denominator) 661 /// \return a new c2_function 662 c2_ratio_p<float_type> &operator / (const c2_function<float_type> &rhs) const throw(c2_exception) 663 { return *new c2_ratio_p<float_type>(get(), rhs); } 664 /// \brief compose this function outside another. 665 /// \param inner the inner function 666 /// \return the composed function 667 c2_composed_function_p<float_type> & operator ()(const c2_function<float_type> &inner) const throw(c2_exception) 668 { return *new c2_composed_function_p<float_type>(get(), inner); } 669 670 protected: 671 const c2_function<float_type> * func; 672 }; 673 674 /// \brief create a container for a c2_function which handles the reference counting. 675 /// \ingroup containers 676 /// 677 /// \see c2_const_ptr and \ref memory_management "Use of c2_ptr for memory management" 678 679 template <typename float_type> class c2_ptr : public c2_const_ptr<float_type > 680 { 681 public: 682 /// \brief construct the container with no function 683 c2_ptr() : c2_const_ptr<float_type>() {} 684 /// \brief construct the container with a pre-defined function 685 /// \param f the function to store 686 c2_ptr(c2_function<float_type> &f) : 687 c2_const_ptr<float_type>() { set_function(&f); } 688 /// \brief copy constructor 689 /// \param src the container to copy 690 c2_ptr(const c2_ptr<float_type> &src) : 691 c2_const_ptr<float_type>() { set_function(src.get_ptr()); } 692 /// \brief get a checked pointer to our owned function 693 inline c2_function<float_type> &get() const throw(c2_exception) 694 { return *const_cast<c2_function<float_type>*>(&c2_const_ptr<float_type>::get()); } 695 /// \brief get an unchecked pointer to our owned function 696 inline c2_function<float_type> *get_ptr() const 697 { return const_cast<c2_function<float_type>*>(this->func); } 698 /// \brief get a checked pointer to our owned function 699 inline c2_function<float_type> *operator -> () const 700 { return &get(); } 701 /// \brief fill the container from another container 702 /// \param f the container to copy 703 void operator =(const c2_ptr<float_type> &f) 704 { set_function(f.get_ptr()); } 705 /// \brief fill the container with a function 706 /// \param f the function 707 void operator =(c2_function<float_type> &f) 708 { set_function(&f); } 709 private: 710 /// \brief hidden non-const-safe version of operator= 711 void operator =(const c2_const_ptr<float_type> &f) { } 712 /// \brief hidden non-const-safe version of operator= 713 void operator =(const c2_function<float_type> &f) { } 714 }; 715 716 /// \brief create a non-generic container for a c2_function which handles the reference counting. 717 /// \ingroup containers 718 /// 719 /// \see c2_const_ptr and \ref memory_management "Use of c2_ptr for memory management" 720 /// 721 /// \note Overuse of this class will generate massive bloat. Use c2_ptr and c2_const_ptr if you don't _really_ need specific pointer types. 722 /// \see \ref memory_management "Use of c2_ptr for memory management" 723 /* 724 template <typename float_type, template <typename> class c2_class > class c2_typed_ptr : public c2_const_ptr<float_type> { 725 public: 726 /// \brief construct the container with no function 727 c2_typed_ptr() : c2_ptr<float_type>() {} 728 /// \brief construct the container with a pre-defined function 729 /// \param f the function to store 730 c2_typed_ptr(c2_class<float_type> &f) 731 : c2_const_ptr<float_type>() { this->set_function(&f); } 732 /// \brief copy constructor 733 /// \param src the container to copy 734 c2_typed_ptr(const c2_typed_ptr<float_type, c2_class> &src) 735 : c2_const_ptr<float_type>() { this->set_function(src.get_ptr()); } 736 737 /// \brief get a reference to our owned function 738 inline c2_class<float_type> &get() const throw(c2_exception) 739 { 740 return *static_cast<c2_class<float_type> *>(const_cast<c2_function<float_type>*>(&c2_const_ptr<float_type>::get())); 741 } 742 /// \brief get a checked pointer to our owned function 743 inline c2_class<float_type> *operator -> () const 744 { return &get(); } 745 /// \brief get an unchecked pointer to our owned function 746 inline c2_class<float_type> *get_ptr() const 747 { return static_cast<c2_class<float_type> *>(const_cast<c2_function<float_type>*>(this->func)); } 748 /// \brief type coercion operator which lets us use a pointer as if it were a c2_function 749 operator c2_class<float_type>&() const { return get(); } 750 /// \brief fill the container from another container 751 /// \param f the container to copy 752 void operator =(const c2_typed_ptr<float_type, c2_class> &f) 753 { set_function(f.get_ptr()); } 754 /// \brief fill the container with a function 755 /// \param f the function 756 void operator =(c2_class<float_type> &f) 757 { set_function(&f); } 758 private: 759 /// \brief hidden downcasting version of operator= 760 void operator =(const c2_const_ptr<float_type> &f) { } 761 /// \brief hidden downcasting version of operator=. Use an explicit dynamic_cast<c2_class<float_type>&>(f) if you need to try this. 762 void operator =(const c2_function<float_type> &f) { } 763 }; 764 */ 328 765 /// \brief a container into which any other c2_function can be dropped, to allow expressions 329 766 /// with replacable components. 330 /// 767 /// \ingroup containers 331 768 ///It is useful for plugging different InterpolatingFunctions into a c2_function expression. 332 769 ///It saves a lot of effort in other places with casting away const declarations. … … 334 771 /// It is also useful as a wrapper for a function if it is necessary to have a copy of a function 335 772 /// which has a different domain or sampling grid than the parent function. This can be 336 /// be used, for example, to patch badly-behaved functions with c2_piecewise_function by773 /// be used, for example, to patch badly-behaved functions with c2_piecewise_function_p by 337 774 /// taking the parent function, creating two plugins of it with domains on each side of the 338 775 /// nasty bit, and then inserting a nice function in the hole. 339 template <typename float_type=double> class c2_plugin_function : public c2_function<float_type> { 776 /// 777 /// This can also be used as a fancier c2_ptr which allows direct evaluation 778 /// instead of having to dereference the container first. 779 /// 780 /// The factory function c2_factory::plugin_function() creates *new c2_plugin_function_p() 781 template <typename float_type=double> class c2_plugin_function_p : 782 public c2_function<float_type> { 340 783 public: 341 784 /// \brief construct the container with no function 342 c2_plugin_function () : c2_function<float_type>(), func(0), owns(false) {}785 c2_plugin_function_p() : c2_function<float_type>(), func() {} 343 786 /// \brief construct the container with a pre-defined function 344 c2_plugin_function (const c2_function<float_type> &f) : c2_function<float_type>(), func(0), owns(false) { set_function(f); }345 /// \brief fill the container with a new function346 void set_function(const c2_function<float_type> &f)347 {348 if(owns && func) delete func;349 func=&f; set_domain(f.xmin(), f.xmax());350 set_sampling_grid_pointer(*f.sampling_grid);351 owns=false;787 c2_plugin_function_p(c2_function<float_type> &f) : 788 c2_function<float_type>(),func(f) { } 789 /// \brief fill the container with a new function, or clear it with a null pointer 790 /// and copy our domain 791 void set_function(c2_function<float_type> *f) 792 { 793 func.set_function(f); 794 if(f) set_domain(f->xmin(), f->xmax()); 352 795 } 353 796 /// \copydoc c2_function::value_with_derivatives … … 355 798 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 356 799 { 357 if(!func ) throw c2_exception("c2_plugin_function<float_type>called uninitialized");358 return this->func->value_with_derivatives(x, yprime, yprime2);800 if(!func.valid()) throw c2_exception("c2_plugin_function_p called uninitialized"); 801 return func->value_with_derivatives(x, yprime, yprime2); 359 802 } 360 /// \brief clear the function361 ///362 /// Any attempt to use this c2_plugin_function throws an exception if the saved function is cleared.363 void unset_function(void) { if(owns && func) delete func; func=0; owns=false; }364 803 /// \brief destructor 365 ~c2_plugin_function() { if(owns && func) delete func; } 366 /// \brief tell us we should delete the function at destruction. NOT sticky when function is reset 367 void set_ownership() { this->owns=true; } 368 804 ~c2_plugin_function_p() { } 805 806 /// \brief clear our function 807 void unset_function() { func.unset_function(); } 808 809 virtual void get_sampling_grid(float_type xmin, float_type xmax, std::vector<float_type> &grid) const 810 { 811 if(!func.valid()) throw c2_exception("c2_plugin_function_p called uninitialized"); 812 if(this->sampling_grid) c2_function<float_type>::get_sampling_grid(xmin, xmax, grid); 813 else func->get_sampling_grid(xmin, xmax, grid); 814 } 369 815 protected: 370 const c2_function<float_type> *func; 371 bool owns; 372 816 c2_ptr<float_type> func; 817 }; 818 819 /// \brief a c2_plugin_function_p which promises not to fiddle with the plugged function. 820 /// \ingroup containers 821 /// 822 /// The factory function c2_factory::const_plugin_function() creates *new c2_const_plugin_function_p() 823 template <typename float_type=double> class c2_const_plugin_function_p : public c2_plugin_function_p<float_type> { 824 public: 825 /// \brief construct the container with no function 826 c2_const_plugin_function_p() : c2_plugin_function_p<float_type>() {} 827 /// \brief construct the container with a pre-defined function 828 c2_const_plugin_function_p(const c2_function<float_type> &f) : 829 c2_plugin_function_p<float_type>() { set_function(&f); } 830 /// \brief fill the container with a new function, or clear it with a null pointer 831 void set_function(const c2_function<float_type> *f) 832 { c2_plugin_function_p<float_type>::set_function(const_cast<c2_function<float_type>*>(f)); } 833 /// \brief destructor 834 ~c2_const_plugin_function_p() { } 835 836 /// \brief get a const reference to our owned function, for direct access 837 const c2_function<float_type> &get() const throw(c2_exception) 838 { return this->func.get(); } 373 839 }; 374 840 375 841 /// \brief Provides support for c2_function objects which are constructed from two other c2_function 376 842 /// objects. 377 /// 378 /// It provides a very primitive ownership concept, so that the creator can tag a function 379 /// as being owned by this function, so that when this function is deleted, the owned function will be deleted, too. 380 /// This allows a piece of code to create various c2_function objects, combine them with binary operators, 381 /// appropriately mark wich ones have no other possible owners, and return the final function with 382 /// reasonable faith that everything will get cleaned up when the final function is deleted. Note that 383 /// none of this marking is automatic, to keep this class very lightweight. 843 /// \ingroup abstract_classes 384 844 template <typename float_type=double> class c2_binary_function : public c2_function<float_type> { 385 845 public: 386 387 388 846 /// \brief function to manage the binary operation, used by c2_binary_function::value_with_derivatives() 389 847 /// 390 /// Normally not used alone, but can be used to combine functions in other contexts.391 /// See interpolating_function::binary_operator() for an example.392 /// \param left the function on the left of the binary operator or outside the composition393 /// \param right the function to the right of the operator or inside the composition394 /// \param[in] x the point at which to evaluate the function395 /// \param[out] yprime the first derivative (if pointer is non-null)396 /// \param[out] yprime2 the second derivative (if pointer is non-null)397 /// \return the value of the function398 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,399 float_type x, float_type *yprime, float_type *yprime2) const =0;400 848 401 849 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception) 402 850 { 403 return this->combine(this->Left, this->Right, x, yprime, yprime2); 851 if(stub) throw c2_exception("attempt to evaluate a c2_binary_function stub"); 852 return this->combine(*Left.get_ptr(), *Right.get_ptr(), x, yprime, yprime2); 404 853 } 405 854 406 /// \brief allow c2_binary_function to remember ownership of contained functions for automatic cleanup855 /// \brief destructor releases ownership of member functions 407 856 /// 408 /// upon destruction, this will cause disposal of the left member function 409 void set_left_ownership(void) { leftown=true; } 410 /// \brief allow c2_binary_function to remember ownership of contained functions for automatic cleanup 411 /// 412 /// upon destruction, this will cause disposal of the right member function 413 void set_right_ownership(void) { rightown=true; } 414 /// \brief allow c2_binary_function to remember ownership of contained functions for automatic cleanup 415 /// 416 /// upon destruction, this will cause disposal of both member functions 417 void set_ownership(void) { leftown=rightown=true; } 418 /// \brief destructor executes disposal of member functions if flagged 419 /// 420 /// depends on judicious use of set_ownership(), set_right_ownership(), or set_left_ownership() 421 virtual ~c2_binary_function() { 422 if(leftown) delete &Left; 423 if(rightown) delete &Right; 424 } 425 857 virtual ~c2_binary_function() { } 858 426 859 protected: 427 860 /// \brief construct the binary function 861 /// \param combiner pointer to the function which actualy knows how to execute the binary 428 862 /// \param left the c2_function to be used in the left side of the binary relation 429 863 /// \param right the c2_function to be used in the right side of the binary relation 430 c2_binary_function( const c2_function<float_type> &left, const c2_function<float_type> &right) : 431 c2_function<float_type>(), 432 Left(left), Right(right), leftown(false), rightown(false) 864 c2_binary_function( 865 float_type (*combiner)(const c2_function<float_type> &left, const c2_function<float_type> &right, 866 float_type x, float_type *yprime, float_type *yprime2), 867 const c2_function<float_type> &left, const c2_function<float_type> &right) : 868 c2_function<float_type>(), combine(combiner), Left(left), Right(right), stub(false) 433 869 { 434 435 436 437 870 set_domain( 871 (left.xmin() > right.xmin()) ? left.xmin() : right.xmin(), 872 (left.xmax() < right.xmax()) ? left.xmax() : right.xmax() 873 ); 438 874 } 439 875 440 876 /// \brief construct a 'stub' c2_binary_function, which provides access to the combine() function 441 877 /// \note Do not evaluate a 'stub' ever. It is only used so that combine() can be called 442 c2_binary_function() : c2_function<float_type>(), 443 Left(*((c2_function<float_type> *)0)), Right(*((c2_function<float_type> *)0)) {} 444 445 const c2_function<float_type> &Left, &Right; 446 bool leftown, rightown; 447 878 c2_binary_function( 879 float_type (*combiner)(const c2_function<float_type> &left, const c2_function<float_type> &right, 880 float_type x, float_type *yprime, float_type *yprime2) 881 ) : c2_function<float_type>(), combine(combiner), Left(), Right(), stub(true) { } 882 883 public: 884 float_type (* const combine)(const c2_function<float_type> &left, const c2_function<float_type> &right, 885 float_type x, float_type *yprime, float_type *yprime2); 886 887 protected: 888 const c2_const_ptr<float_type> Left, Right; 889 /// \brief if true, we don't own any functions, we are just a source of a combining function. 890 bool stub; 891 448 892 }; 449 893 450 894 /// \brief Create a very lightweight method to return a scalar multiple of another function. 451 /// 452 template <typename float_type=double> class c2_scaled_function : public c2_plugin_function<float_type> { 895 /// \ingroup containers \ingroup arithmetic_functions \ingroup parametric_functions 896 /// 897 /// The factory function c2_factory::scaled_function() creates *new c2_scaled_function_p 898 template <typename float_type=double> class c2_scaled_function_p : public c2_function<float_type> { 453 899 public: 454 900 /// \brief construct the function with its scale factor. … … 456 902 /// \param outer the function to be scaled 457 903 /// \param scale the multiplicative scale factor 458 c2_scaled_function (const c2_function<float_type> &outer, float_type scale) :459 c2_ plugin_function<float_type>(outer), yscale(scale) { }904 c2_scaled_function_p(const c2_function<float_type> &outer, float_type scale) : 905 c2_function<float_type>(), func(outer), yscale(scale) { } 460 906 461 907 /// \brief set a new scale factor … … 475 921 476 922 protected: 477 c2_scaled_function<float_type>() {} // hide default constructor, since its use is almost always an error. 923 c2_scaled_function_p<float_type>() : func() {} // hide default constructor, since its use is almost always an error. 924 /// \brief the scaling factor for the function 925 const c2_const_ptr<float_type> func; 478 926 float_type yscale; 479 927 }; 480 928 481 929 /// \brief A container into which any other c2_function can be dropped. 482 /// 930 /// \ingroup containers 483 931 /// It allows a function to be pre-evaluated at a point, and used at multiple places in an expression 484 932 /// efficiently. If it is re-evaluated at the previous point, it returns the remembered values; 485 933 /// otherwise, it re-evauates the function at the new point. 486 934 /// 487 template <typename float_type=double> class c2_cached_function : public c2_plugin_function<float_type> { 935 /// The factory function c2_factory::cached_function() creates *new c2_cached_function_p 936 template <typename float_type=double> class c2_cached_function_p : public c2_function<float_type> { 488 937 public: 489 938 /// \brief construct the container 490 939 /// 491 940 /// \param f the function to be cached 492 c2_cached_function(const c2_function<float_type> &f) : c2_plugin_function<float_type>(f), init(false) {} 941 c2_cached_function_p(const c2_function<float_type> &f) : c2_function<float_type>(), 942 func(f), init(false) {} 493 943 /// \copydoc c2_function::value_with_derivatives 494 944 /// … … 508 958 509 959 protected: 510 c2_cached_function() {} // hide default constructor, since its use is almost always an error. 960 c2_cached_function_p() : func() {} // hide default constructor, since its use is almost always an error. 961 const c2_const_ptr<float_type> func; 511 962 mutable bool init; 512 963 mutable float_type x0, y, yp, ypp; … … 515 966 516 967 /// \brief Provides function composition (nesting) 517 /// 968 /// \ingroup arithmetic_functions 518 969 /// This allows evaluation of \a f(g(x)) where \a f and \a g are c2_function objects. 519 970 /// 520 /// \note See c2_binary_function for discussion of ownership.521 template <typename float_type=double> class c2_composed_function : public c2_binary_function<float_type> {971 /// This should always be constructed using \ref compose_operator "c2_function::operator()" 972 template <typename float_type=double> class c2_composed_function_p : public c2_binary_function<float_type> { 522 973 public: 523 974 … … 526 977 /// \param outer the outer function 527 978 /// \param inner the inner function 528 c2_composed_function(const c2_function<float_type> &outer, const c2_function<float_type> &inner) : c2_binary_function<float_type>(outer, inner) {} 979 c2_composed_function_p(const c2_function<float_type> &outer, const c2_function<float_type> &inner) : 980 c2_binary_function<float_type>(combine, outer, inner) { this->set_domain(inner.xmin(), inner.xmax()); } 529 981 /// \brief Create a stub just for the combiner to avoid statics. 530 c2_composed_function() : c2_binary_function<float_type>() {} ; 531 532 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right, 533 float_type x, float_type *yprime, float_type *yprime2) const 982 c2_composed_function_p() : c2_binary_function<float_type>(combine) {} 983 984 /// \brief execute math necessary to do composition 985 static float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right, 986 float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) 534 987 { 535 float_type y0, yp0, ypp0, y1, yp1, ypp1; 536 float_type *yp0p, *ypp0p, *yp1p, *ypp1p; 988 float_type y0, y1; 537 989 if(yprime || yprime2) { 538 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1; 990 float_type yp0, ypp0, yp1, ypp1; 991 y0=right.value_with_derivatives(x, &yp0, &ypp0); 992 y1=left.value_with_derivatives(y0, &yp1, &ypp1); 993 if(yprime) *yprime=yp1*yp0; 994 if(yprime2) *yprime2=ypp0*yp1+yp0*yp0*ypp1; 539 995 } else { 540 yp0p=ypp0p=yp1p=ypp1p=0; 996 y0=right(x); 997 y1=left(y0); 541 998 } 542 543 y0=right.value_with_derivatives(x, yp0p, ypp0p);544 y1=left.value_with_derivatives(y0, yp1p, ypp1p);545 if(yprime) *yprime=yp1*yp0;546 if(yprime2) *yprime2=ypp0*yp1+yp0*yp0*ypp1;547 999 return y1; 548 1000 } … … 550 1002 551 1003 /// \brief create a c2_function which is the sum of two other c2_function objects. 552 /// 553 /// \note See c2_binary_function for discussion of ownership.554 template <typename float_type=double> class c2_sum : public c2_binary_function<float_type> {1004 /// \ingroup arithmetic_functions 1005 /// This should always be constructed using c2_function::operator+() 1006 template <typename float_type=double> class c2_sum_p : public c2_binary_function<float_type> { 555 1007 public: 556 1008 /// \brief construct \a left + \a right 557 /// \note See c2_binary_function for discussion of ownership. 1009 /// \param left the left function 1010 /// \param right the right function 1011 c2_sum_p(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(combine, left, right) {} 1012 /// \brief Create a stub just for the combiner to avoid statics. 1013 c2_sum_p() : c2_binary_function<float_type>(combine) {} ; // create a stub just for the combiner to avoid statics 1014 1015 /// \brief execute math necessary to do addition 1016 static float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right, 1017 float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) 1018 { 1019 float_type y0, y1; 1020 if(yprime || yprime2) { 1021 float_type yp0, ypp0, yp1, ypp1; 1022 y0=left.value_with_derivatives(x, &yp0, &ypp0); 1023 y1=right.value_with_derivatives(x, &yp1, &ypp1); 1024 if(yprime) *yprime=yp0+yp1; 1025 if(yprime2) *yprime2=ypp0+ypp1; 1026 } else { 1027 y0=left(x); 1028 y1=right(x); 1029 } 1030 return y0+y1; 1031 } 1032 }; 1033 1034 1035 /// \brief create a c2_function which is the difference of two other c2_functions. 1036 /// \ingroup arithmetic_functions 1037 /// This should always be constructed using c2_function::operator-() 1038 template <typename float_type=double> class c2_diff_p : public c2_binary_function<float_type> { 1039 public: 1040 /// \brief construct \a left - \a right 558 1041 /// \param left the left function 559 1042 /// \param right the right function 560 c2_ sum(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}1043 c2_diff_p(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(combine, left, right) {} 561 1044 /// \brief Create a stub just for the combiner to avoid statics. 562 c2_ sum() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics563 564 // function to do derivative arithmetic for sums565 virtualfloat_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,566 float_type x, float_type *yprime, float_type *yprime2) const1045 c2_diff_p() : c2_binary_function<float_type>(combine) {} ; // create a stub just for the combiner to avoid statics 1046 1047 /// \brief execute math necessary to do subtraction 1048 static float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right, 1049 float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) 567 1050 { 568 float_type y0, yp0, ypp0, y1, yp1, ypp1; 569 float_type *yp0p, *ypp0p, *yp1p, *ypp1p; 1051 float_type y0, y1; 570 1052 if(yprime || yprime2) { 571 yp0p=&yp0; ypp0p=& ypp0; yp1p=&yp1; ypp1p=&ypp1; 1053 float_type yp0, ypp0, yp1, ypp1; 1054 y0=left.value_with_derivatives(x, &yp0, &ypp0); 1055 y1=right.value_with_derivatives(x, &yp1, &ypp1); 1056 if(yprime) *yprime=yp0-yp1; 1057 if(yprime2) *yprime2=ypp0-ypp1; 572 1058 } else { 573 yp0p=ypp0p=yp1p=ypp1p=0; 1059 y0=left(x); 1060 y1=right(x); 574 1061 } 575 y0=left.value_with_derivatives(x, yp0p, ypp0p); 576 y1=right.value_with_derivatives(x, yp1p, ypp1p); 577 if(yprime) *yprime=yp0+yp1; 578 if(yprime2) *yprime2=ypp0+ypp1; 579 return y0+y1; 1062 return y0-y1; 580 1063 } 581 1064 }; 582 1065 583 1066 584 /// \brief create a c2_function which is the differenceof two other c2_functions.585 /// 586 /// \note See c2_binary_function for discussion of ownership.587 template <typename float_type=double> class c2_ diff: public c2_binary_function<float_type> {1067 /// \brief create a c2_function which is the product of two other c2_functions. 1068 /// \ingroup arithmetic_functions 1069 /// This should always be constructed using c2_function::operator*() 1070 template <typename float_type=double> class c2_product_p : public c2_binary_function<float_type> { 588 1071 public: 589 /// \brief construct \a left - \a right 590 /// \note See c2_binary_function for discussion of ownership. 1072 /// \brief construct \a left * \a right 591 1073 /// \param left the left function 592 1074 /// \param right the right function 593 c2_ diff(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}1075 c2_product_p(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(combine, left, right) {} 594 1076 /// \brief Create a stub just for the combiner to avoid statics. 595 c2_ diff() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics596 597 // function to do derivative arithmetic for diffs598 virtualfloat_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,599 float_type x, float_type *yprime, float_type *yprime2) const1077 c2_product_p() : c2_binary_function<float_type>(combine) {} ; // create a stub just for the combiner to avoid statics 1078 1079 /// \brief execute math necessary to do multiplication 1080 static float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right, 1081 float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) 600 1082 { 601 float_type y0, yp0, ypp0, y1, yp1, ypp1; 602 float_type *yp0p, *ypp0p, *yp1p, *ypp1p; 1083 float_type y0, y1; 603 1084 if(yprime || yprime2) { 604 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1; 1085 float_type yp0, ypp0, yp1, ypp1; 1086 y0=left.value_with_derivatives(x, &yp0, &ypp0); 1087 y1=right.value_with_derivatives(x, &yp1, &ypp1); 1088 if(yprime) *yprime=y1*yp0+y0*yp1; 1089 if(yprime2) *yprime2=ypp0*y1+2.0*yp0*yp1+ypp1*y0; 605 1090 } else { 606 yp0p=ypp0p=yp1p=ypp1p=0; 1091 y0=left(x); 1092 y1=right(x); 607 1093 } 608 y0=left.value_with_derivatives(x, yp0p, ypp0p); 609 y1=right.value_with_derivatives(x, yp1p, ypp1p); 610 if(yprime) *yprime=yp0-yp1; 611 if(yprime2) *yprime2=ypp0-ypp1; 612 return y0-y1; 1094 return y0*y1; 613 1095 } 614 1096 }; 615 1097 616 1098 617 /// \brief create a c2_function which is the productof two other c2_functions.618 /// 619 /// \note See c2_binary_function for discussion of ownership.620 template <typename float_type=double> class c2_ product: public c2_binary_function<float_type> {1099 /// \brief create a c2_function which is the ratio of two other c2_functions. 1100 /// \ingroup arithmetic_functions 1101 /// This should always be constructed using c2_function::operator/() 1102 template <typename float_type=double> class c2_ratio_p : public c2_binary_function<float_type> { 621 1103 public: 622 /// \brief construct \a left * \a right 623 /// \note See c2_binary_function for discussion of ownership. 1104 /// \brief construct \a left / \a right 624 1105 /// \param left the left function 625 1106 /// \param right the right function 626 c2_ product(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}1107 c2_ratio_p(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(combine, left, right) {} 627 1108 /// \brief Create a stub just for the combiner to avoid statics. 628 c2_product() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics 629 630 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right, 631 float_type x, float_type *yprime, float_type *yprime2) const 1109 c2_ratio_p() : c2_binary_function<float_type>(combine) {} ; // create a stub just for the combiner to avoid statics 1110 1111 /// \brief execute math necessary to do division 1112 static float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right, 1113 float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) 632 1114 { 633 float_type y0, yp0, ypp0, y1, yp1, ypp1; 634 float_type *yp0p, *ypp0p, *yp1p, *ypp1p; 1115 float_type y0, y1; 635 1116 if(yprime || yprime2) { 636 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1; 1117 float_type yp0, ypp0, yp1, ypp1; 1118 y0=left.value_with_derivatives(x, &yp0, &ypp0); 1119 y1=right.value_with_derivatives(x, &yp1, &ypp1); 1120 if(yprime) *yprime=(yp0*y1-y0*yp1)/(y1*y1); // first deriv of ratio 1121 if(yprime2) *yprime2=(y1*y1*ypp0+y0*(2*yp1*yp1-y1*ypp1)-2*y1*yp0*yp1)/(y1*y1*y1); 637 1122 } else { 638 yp0p=ypp0p=yp1p=ypp1p=0; 1123 y0=left(x); 1124 y1=right(x); 639 1125 } 640 y0=left.value_with_derivatives(x, yp0p, ypp0p);641 y1=right.value_with_derivatives(x, yp1p, ypp1p);642 if(yprime) *yprime=y1*yp0+y0*yp1;643 if(yprime2) *yprime2=ypp0*y1+2.0*yp0*yp1+ypp1*y0;644 return y0*y1;645 }646 };647 648 649 /// \brief create a c2_function which is the ratio of two other c2_functions.650 ///651 /// \note See c2_binary_function for discussion of ownership.652 template <typename float_type=double> class c2_ratio : public c2_binary_function<float_type> {653 public:654 /// \brief construct \a left / \a right655 /// \note See c2_binary_function for discussion of ownership.656 /// \param left the left function657 /// \param right the right function658 c2_ratio(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}659 /// \brief Create a stub just for the combiner to avoid statics.660 c2_ratio() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics661 662 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,663 float_type x, float_type *yprime, float_type *yprime2) const664 {665 float_type y0, yp0, ypp0, y1, yp1, ypp1;666 float_type *yp0p, *ypp0p, *yp1p, *ypp1p;667 if(yprime || yprime2) {668 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1;669 } else {670 yp0p=ypp0p=yp1p=ypp1p=0;671 }672 y0=left.value_with_derivatives(x, yp0p, ypp0p);673 y1=right.value_with_derivatives(x, yp1p, ypp1p);674 if(yprime) *yprime=(yp0*y1-y0*yp1)/(y1*y1); // first deriv of ratio675 if(yprime2) *yprime2=(y1*y1*ypp0+y0*(2*yp1*yp1-y1*ypp1)-2*y1*yp0*yp1)/(y1*y1*y1); // second deriv of ratio676 1126 return y0/y1; 677 1127 } … … 679 1129 }; 680 1130 681 /// \brief a c2_function which is constant : can do interpolating_function f2=f1 + c2_constant(11.3) 682 template <typename float_type> class c2_constant : public c2_function<float_type> { 683 public: 684 c2_constant(float_type x=0.0) : c2_function<float_type>(), value(x) {} 1131 /// \brief a c2_function which is constant 1132 /// \ingroup parametric_functions 1133 /// 1134 /// The factory function c2_factory::constant() creates *new c2_constant_p() 1135 template <typename float_type> class c2_constant_p : public c2_function<float_type> { 1136 public: 1137 c2_constant_p(float_type x) : c2_function<float_type>(), value(x) {} 685 1138 void reset(float_type val) { value=val; } 686 1139 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) … … 691 1144 }; 692 1145 1146 /// \brief a transformation of a coordinate, including an inverse 1147 /// \ingroup transforms 1148 template <typename float_type> class c2_transformation { 1149 public: 1150 /// \brief initialize all our function pointers 1151 /// \param transformed true if this function is not the identity 1152 /// \param xin input X transform 1153 /// \param xinp input X transform derivative 1154 /// \param xinpp input X transform second derivative 1155 /// \param xout output X transform, which MUST be the inverse of \a xin 1156 c2_transformation(bool transformed, 1157 float_type (*xin)(float_type), float_type (*xinp)(float_type), float_type (*xinpp)(float_type), float_type (*xout)(float_type) 1158 ) : 1159 fTransformed(transformed), fHasStaticTransforms(true), 1160 pIn(xin), pInPrime(xinp), pInDPrime(xinpp), pOut(xout) { } 1161 1162 /// \brief initialize all our function pointers so that only the (overridden) virtual functions can be called without an error 1163 /// \param transformed true if this function is nonlinear 1164 c2_transformation(bool transformed) : 1165 fTransformed(transformed), fHasStaticTransforms(false), 1166 pIn(report_error), pInPrime(report_error), pInDPrime(report_error), pOut(report_error) { } 1167 /// \brief the destructor 1168 virtual ~c2_transformation() { } 1169 /// \brief flag to indicate if this transform is not the identity 1170 const bool fTransformed; 1171 /// \brief flag to indicate if the static function pointers can be used for efficiency 1172 const bool fHasStaticTransforms; 1173 1174 /// \note the pointers to functions allow highly optimized access when static functions are available. 1175 /// They are only used inside value_with_derivatives(), which is assumed to be the most critical routine. 1176 /// \brief non-virtual pointer to input X transform 1177 float_type (* const pIn)(float_type); 1178 /// \brief non-virtual pointer to input X transform derivative 1179 float_type (* const pInPrime)(float_type); 1180 /// \brief non-virtual pointer to input X transform second derivative 1181 float_type (* const pInDPrime)(float_type); 1182 /// \brief non-virtual pointer to output X transform 1183 float_type (* const pOut)(float_type); 1184 1185 /// \brief virtual input X transform 1186 virtual float_type fIn(float_type x) const { return pIn(x); } 1187 /// \brief virtual input X transform derivative 1188 virtual float_type fInPrime(float_type x) const { return pInPrime(x); } 1189 /// \brief virtual input X transform second derivative 1190 virtual float_type fInDPrime(float_type x) const { return pInDPrime(x); } 1191 /// \brief virtual output X transform 1192 virtual float_type fOut(float_type x) const { return pOut(x); } 1193 1194 protected: 1195 /// \brief utility function for unimplemented conversion 1196 static float_type report_error(float_type x) { throw c2_exception("use of improperly constructed axis transform"); return x; } 1197 /// \brief utility function f(x)=x useful in axis transforms 1198 static float_type ident(float_type x) { return x; } 1199 /// \brief utility function f(x)=1 useful in axis transforms 1200 static float_type one(float_type) { return 1; } 1201 /// \brief utility function f(x)=0 useful in axis transforms 1202 static float_type zero(float_type) { return 0; } 1203 /// \brief utility function f(x)=1/x useful in axis transforms 1204 static float_type recip(float_type x) { return 1.0/x; } 1205 /// \brief utility function f(x)=-1/x**2 useful in axis transforms 1206 static float_type recip_prime(float_type x) { return -1/(x*x); } 1207 /// \brief utility function f(x)=2/x**3 useful in axis transforms 1208 static float_type recip_prime2(float_type x) { return 2/(x*x*x); } 1209 1210 }; 1211 1212 /// \brief the identity transform 1213 /// \ingroup transforms 1214 template <typename float_type> class c2_transformation_linear : public c2_transformation<float_type> { 1215 public: 1216 /// \brief constructor 1217 c2_transformation_linear() : c2_transformation<float_type>(false, this->ident, this->one, this->zero, this->ident) { } 1218 /// \brief destructor 1219 ~c2_transformation_linear() { } 1220 }; 1221 /// \brief log axis transform 1222 /// \ingroup transforms 1223 template <typename float_type> class c2_transformation_log : public c2_transformation<float_type> { 1224 public: 1225 /// \brief constructor 1226 c2_transformation_log() : c2_transformation<float_type>(true, std::log, this->recip, this->recip_prime, std::exp) { } 1227 /// \brief destructor 1228 ~c2_transformation_log() { } 1229 }; 1230 /// \brief reciprocal axis transform 1231 /// \ingroup transforms 1232 template <typename float_type> class c2_transformation_recip : public c2_transformation<float_type> { 1233 public: 1234 /// \brief constructor 1235 c2_transformation_recip() : c2_transformation<float_type>(true, this->recip, this->recip_prime, this->recip_prime2, this->recip) { } 1236 /// \brief destructor 1237 ~c2_transformation_recip() { } 1238 }; 1239 1240 /// \brief a transformation of a function in and out of a coordinate space, using 2 c2_transformations 1241 /// 1242 /// This class is a container for two axis transforms, but also provides the critical evaluate() 1243 /// function which converts a result in internal coordinates (with derivatives) into the external representation 1244 /// \ingroup transforms 1245 template <typename float_type> 1246 class c2_function_transformation { 1247 public: 1248 /// \brief construct this from two c2_transformation instances 1249 /// \param xx the X axis transform 1250 /// \param yy the Y axis transform 1251 c2_function_transformation( 1252 const c2_transformation<float_type> &xx, const c2_transformation<float_type> &yy) : 1253 isIdentity(!(xx.fTransformed || yy.fTransformed)), X(xx), Y(yy) { } 1254 /// \brief destructor 1255 virtual ~c2_function_transformation() { delete &X; delete &Y; } 1256 /// \brief evaluate the transformation from internal coordinates to external coordinates 1257 /// \param xraw the value of \a x in external cordinates at which the transform is taking place 1258 /// \param y the value of the function in internal coordinates 1259 /// \param yp0 the derivative in internal coordinates 1260 /// \param ypp0 the second derivative in internal coordinates 1261 /// \param [out] yprime pointer to the derivative, or NULL, in external coordinates 1262 /// \param [out] yprime2 pointer to the second derivative, or NULL, in external coordinates 1263 /// \return the value of the function in external coordinates 1264 virtual float_type evaluate(float_type xraw, 1265 float_type y, float_type yp0, float_type ypp0, 1266 float_type *yprime, float_type *yprime2) const; 1267 /// \brief flag indicating of the transform is the identity, and can be skipped for efficiency 1268 const bool isIdentity; 1269 /// \brief the X axis transform 1270 const c2_transformation<float_type> &X; 1271 /// \brief the Y axis transform 1272 const c2_transformation<float_type> &Y; 1273 }; 1274 1275 /// \brief a transformation of a function in and out of lin-lin space 1276 /// 1277 /// \ingroup transforms 1278 template <typename float_type> class c2_lin_lin_function_transformation : 1279 public c2_function_transformation<float_type> { 1280 public: 1281 c2_lin_lin_function_transformation() : 1282 c2_function_transformation<float_type>( 1283 *new c2_transformation_linear<float_type>, 1284 *new c2_transformation_linear<float_type> 1285 ) { } 1286 virtual ~c2_lin_lin_function_transformation() { } 1287 }; 1288 1289 /// \brief a transformation of a function in and out of log-log space 1290 /// 1291 /// \ingroup transforms 1292 template <typename float_type> class c2_log_log_function_transformation : 1293 public c2_function_transformation<float_type> { 1294 public: 1295 c2_log_log_function_transformation() : 1296 c2_function_transformation<float_type>( 1297 *new c2_transformation_log<float_type>, 1298 *new c2_transformation_log<float_type> 1299 ) { } 1300 virtual ~c2_log_log_function_transformation() { } 1301 }; 1302 1303 /// \brief a transformation of a function in and out of lin-log space 1304 /// 1305 /// \ingroup transforms 1306 template <typename float_type> class c2_lin_log_function_transformation : 1307 public c2_function_transformation<float_type> { 1308 public: 1309 c2_lin_log_function_transformation() : 1310 c2_function_transformation<float_type>( 1311 *new c2_transformation_linear<float_type>, 1312 *new c2_transformation_log<float_type> 1313 ) { } 1314 virtual ~c2_lin_log_function_transformation() { } 1315 }; 1316 1317 /// \brief a transformation of a function in and out of log-lin space 1318 /// 1319 /// \ingroup transforms 1320 template <typename float_type> class c2_log_lin_function_transformation : 1321 public c2_function_transformation<float_type> { 1322 public: 1323 c2_log_lin_function_transformation() : 1324 c2_function_transformation<float_type>( 1325 *new c2_transformation_log<float_type>, 1326 *new c2_transformation_linear<float_type> 1327 ) { } 1328 virtual ~c2_log_lin_function_transformation() { } 1329 }; 1330 1331 /// \brief a transformation of a function in and out of Arrhenuis (1/x vs. log(y)) space 1332 /// 1333 /// \ingroup transforms 1334 template <typename float_type> class c2_arrhenius_function_transformation : 1335 public c2_function_transformation<float_type> { 1336 public: 1337 c2_arrhenius_function_transformation() : 1338 c2_function_transformation<float_type>( 1339 *new c2_transformation_recip<float_type>, 1340 *new c2_transformation_log<float_type> 1341 ) { } 1342 virtual ~c2_arrhenius_function_transformation() { } 1343 }; 1344 693 1345 /** 694 1346 \brief create a cubic spline interpolation of a set of (x,y) pairs 695 1347 \ingroup interpolators 696 1348 This is one of the main reasons for c2_function objects to exist. 697 1349 … … 707 1359 (vanishing second derivative), is created as: \n 708 1360 \code 1361 c2_ptr<double> c2p; 1362 c2_factory<double> c2; 709 1363 std::vector<double> xvals(10), yvals(10); 710 1364 // < fill in xvals and yvals > 711 interpolating_function<double> myfunc(xvals, yvals);1365 c2p myfunc=c2.interpolating_function().load(xvals, yvals,true,0,true,0); 712 1366 // and it can be evaluated at a point for its value only by: 713 1367 double y=myfunc(x); … … 716 1370 double y=myfunc(x,&yprime, &yprime2); 717 1371 \endcode 1372 1373 The factory function c2_factory::interpolating_function() creates *new interpolating_function_p() 718 1374 */ 719 1375 720 template <typename float_type=double> class interpolating_function : public c2_function<float_type> {721 public: 722 /// \brief create the most general interpolating_function which defaults to linear-linear space1376 template <typename float_type=double> class interpolating_function_p : public c2_function<float_type> { 1377 public: 1378 /// \brief an empty linear-linear cubic-spline interpolating_function_p 723 1379 /// 724 1380 /// lots to say here, but see Numerical Recipes for a discussion of cubic splines. 1381 /// 1382 interpolating_function_p() : c2_function<float_type>(), 1383 fTransform(*new c2_lin_lin_function_transformation<float_type>) { } 1384 1385 /// \brief an empty cubic-spline interpolating_function_p with a specific transform 1386 /// 1387 interpolating_function_p(const c2_function_transformation<float_type> &transform) : c2_function<float_type>(), 1388 fTransform(transform) { } 1389 1390 /// \brief do the dirty work of constructing the spline from a function. 725 1391 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing. 726 1392 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid. … … 730 1396 /// \param upperSlopeNatural if true, set y''(last point)=0, otherwise compute it from \a upperSope 731 1397 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false 732 /// \param inputXConversion a function (not a c2_function) which converts \a x into the internal space. \n 733 /// If this is NULL, use linear space and ignore inputXConversionPrime, inputXConversionDPrime 734 /// \param inputYConversion a function (not a c2_function) which converts \a y into the internal space. \n 735 /// If this is NULL, use linear space and ignore inputYConversionPrime, inputYConversionDPrime, outputYConversion 736 /// \param outputYConversion a function (not a c2_function) which converts \a y out of the internal space 737 /// \param inputXConversionPrime the derivative of \a inputXConversion 738 /// \param inputYConversionPrime the derivative of \a inputYConversion 739 /// \param inputXConversionDPrime the second derivative of \a inputXConversion 740 /// \param inputYConversionDPrime the second derivative of \a inputYConversion 741 interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f, 742 bool lowerSlopeNatural=true, float_type lowerSlope=0.0, 743 bool upperSlopeNatural=true, float_type upperSlope=0.0, 744 float_type (*inputXConversion)(float_type)=0, 745 float_type (*inputYConversion)(float_type)=0, 746 float_type (*outputYConversion)(float_type)=0, 747 float_type (*inputXConversionPrime)(float_type)=0, 748 float_type (*inputYConversionPrime)(float_type)=0, 749 float_type (*inputXConversionDPrime)(float_type)=0, 750 float_type (*inputYConversionDPrime)(float_type)=0 751 ) throw(c2_exception) : c2_function<float_type>() 752 { init(x, f, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, 753 inputXConversion, inputYConversion, outputYConversion, 754 inputXConversionPrime, inputYConversionPrime, 755 inputXConversionDPrime, inputYConversionDPrime 756 ); 757 } 758 759 /// \brief copy constructor 760 /// \param rhs interpolating_function to copy from 761 interpolating_function(const interpolating_function <float_type> &rhs) : c2_function<float_type>(rhs), 762 Xraw(rhs.Xraw), X(rhs.X), F(rhs.F), y2(rhs.y2), 763 fXin(rhs.fXin), fYin(rhs.fYin), fYout(rhs.fYout), 764 fXinPrime(rhs.fXinPrime), fYinPrime(rhs.fYinPrime), 765 fXinDPrime(rhs.fXinDPrime), fYinDPrime(rhs.fYinDPrime) , 766 xInverted(rhs.xInverted), lastKLow(-1) 767 { set_sampling_grid_pointer(Xraw); } 768 769 virtual ~interpolating_function() { } // just to suppress warnings about no virtual destructor 1398 /// \param splined if true (default), use cubic spline, if false, use linear interpolation. 1399 /// \return the same interpolating function, filled 1400 interpolating_function_p<float_type> & load(const std::vector<float_type> &x, const std::vector<float_type> &f, 1401 bool lowerSlopeNatural, float_type lowerSlope, 1402 bool upperSlopeNatural, float_type upperSlope, bool splined=true 1403 ) throw(c2_exception); 1404 1405 /// \brief do the dirty work of constructing the spline from a function. 1406 /// \param data std::vector of std::pairs of x,y. Will be sorted into x increasing order in place. 1407 /// \param lowerSlopeNatural if true, set y''(first point)=0, otherwise compute it from \a lowerSope 1408 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false 1409 /// \param upperSlopeNatural if true, set y''(last point)=0, otherwise compute it from \a upperSope 1410 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false 1411 /// \param splined if true (default), use cubic spline, if false, use linear interpolation. 1412 /// \return the same interpolating function, filled 1413 interpolating_function_p<float_type> & load_pairs( 1414 std::vector<std::pair<float_type, float_type> > &data, 1415 bool lowerSlopeNatural, float_type lowerSlope, 1416 bool upperSlopeNatural, float_type upperSlope, bool splined=true 1417 ) throw(c2_exception); 1418 1419 /// \brief do the dirty work of constructing the spline from a function. 1420 /// \param func a function without any requirement of valid derivatives to sample into an interpolating function. 1421 /// Very probably a c2_classic_function. 1422 /// \param xmin the lower bound of the region to sample 1423 /// \param xmax the upper bound of the region to sample 1424 /// \param abs_tol the maximum absolute error permitted when linearly interpolating the points. 1425 /// the real error will be much smaller, since this uses cubic splines at the end. 1426 /// \param rel_tol the maximum relative error permitted when linearly interpolating the points. 1427 /// the real error will be much smaller, since this uses cubic splines at the end. 1428 /// \param lowerSlopeNatural if true, set y'(first point) from 3-point parabola, otherwise compute it from \a lowerSope 1429 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false 1430 /// \param upperSlopeNatural if true, set y'(last point) from 3-point parabola, otherwise compute it from \a upperSope 1431 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false 1432 /// \return the same interpolating function, filled 1433 /// \note If the interpolator being filled has a log vertical axis, put the desired relative error in 1434 /// \a abs_tol, and 0 in \a rel_tol since the absolute error on the log of a function is the relative error 1435 /// on the function itself. 1436 interpolating_function_p<float_type> & sample_function(const c2_function<float_type> &func, 1437 float_type xmin, float_type xmax, float_type abs_tol, float_type rel_tol, 1438 bool lowerSlopeNatural, float_type lowerSlope, 1439 bool upperSlopeNatural, float_type upperSlope 1440 ) throw(c2_exception); 1441 1442 1443 /// \brief initialize from a grid of points and a c2_function (un-normalized) to an 1444 /// interpolator which, when evaluated with a uniform random variate on [0,1] returns random numbers 1445 /// distributed as the input function. 1446 /// \see \ref random_subsec "Arbitrary random generation" 1447 /// inverse_integrated_density starts with a probability density std::vector, generates the integral, 1448 /// and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values 1449 /// with a density distribution equal to the input distribution 1450 /// If the data are passed in reverse order (large X first), the integral is carried out from the big end. 1451 /// \param bincenters the positions at which to sample the function \a binheights 1452 /// \param binheights a function which describes the density of the random number distribution to be produced. 1453 /// \return an initialized interpolator, which 1454 /// if evaluated randomly with a uniform variate on [0,1] produces numbers 1455 /// distributed according to \a binheights 1456 interpolating_function_p<float_type> & load_random_generator_function( 1457 const std::vector<float_type> &bincenters, const c2_function<float_type> &binheights) 1458 throw(c2_exception); 1459 1460 /// \brief initialize from a grid of points and an std::vector of probability densities (un-normalized) to an 1461 /// interpolator which, when evaluated with a uniform random variate on [0,1] returns random numbers 1462 /// distributed as the input histogram. 1463 /// \see \ref random_subsec "Arbitrary random generation" 1464 /// inverse_integrated_density starts with a probability density std::vector, generates the integral, 1465 /// and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values 1466 /// with a density distribution equal to the input distribution 1467 /// If the data are passed in reverse order (large X first), the integral is carried out from the big end. 1468 /// \param bins if \a bins .size()==\a binheights .size(), the centers of the bins. \n 1469 /// if \a bins .size()==\a binheights .size()+1, the edges of the bins 1470 /// \param binheights a vector which describes the density of the random number distribution to be produced. 1471 /// Note density... the numbers in the bins are not counts, but counts/unit bin width. 1472 /// \return an initialized interpolator, which 1473 /// if evaluated randomly with a uniform variate on [0,1] produces numbers 1474 /// distributed according to \a binheights 1475 interpolating_function_p<float_type> & load_random_generator_bins( 1476 const std::vector<float_type> &bins, const std::vector<float_type> &binheights) 1477 throw(c2_exception); 1478 1479 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception); 1480 1481 /// \brief destructor 1482 virtual ~interpolating_function_p() { delete &fTransform; } 1483 1484 /// \brief create a new, empty interpolating function of this type (virtual constructor) 1485 virtual interpolating_function_p<float_type> &clone() const throw(c2_exception) 1486 { return *new interpolating_function_p<float_type>(); } 770 1487 771 1488 /// \brief retrieve copies of the x & y tables from which this was built 772 1489 /// 773 1490 /// This is often useful in the creation of new interpolating functions with transformed data. 774 /// The vectors will have their sizes set correctly on return.1491 /// The vectors will have their sizes set correctly on return. 775 1492 /// \param [in, out] xvals the abscissas 776 1493 /// \param [in, out] yvals the ordinates … … 793 1510 // preserving the X bounds and mapping functions of the host (left hand) function. 794 1511 795 /// \brief create a new interpolating_function which is the \a source1512 /// \brief create a new interpolating_function_p which is the \a source 796 1513 /// function applied to every point in the interpolating tables 797 1514 /// 798 1515 /// This carefully manages the derivative of the composed function at the two ends. 799 1516 /// \param source the function to apply 800 /// \return a new interpolating_function with the same mappings for x and y801 interpolating_function <float_type> & unary_operator(const c2_function<float_type> &source) const;802 803 /// \brief create a new interpolating_function which is the parent interpolating_function1517 /// \return a new interpolating_function_p with the same mappings for x and y 1518 interpolating_function_p <float_type> & unary_operator(const c2_function<float_type> &source) const; 1519 1520 /// \brief create a new interpolating_function_p which is the parent interpolating_function_p 804 1521 /// combined with \a rhs using \a combiner at every point in the interpolating tables 805 1522 /// … … 807 1524 /// \param rhs the function to apply 808 1525 /// \param combining_stub a function which defines which binary operation to use. 809 /// \return a new interpolating_function with the same mappings for x and y810 interpolating_function <float_type> & binary_operator(const c2_function<float_type> &rhs,811 c 2_binary_function<float_type> *combining_stub1526 /// \return a new interpolating_function_p with the same mappings for x and y 1527 interpolating_function_p <float_type> & binary_operator(const c2_function<float_type> &rhs, 1528 const c2_binary_function<float_type> *combining_stub 812 1529 ) const; 813 814 // InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 815 // when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 816 // can be upcast back to a c2_function to produce unprocessed binaries. 817 818 /// \brief produce a newly resampled interpolating_function which is the specified sum. 1530 /// \brief produce a newly resampled interpolating_function_p which is the specified sum. 819 1531 /// \param rhs the function to add, pointwise 820 /// \return a new interpolating_function 821 /// \note 822 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 823 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 824 /// can be upcast back to a c2_function to produce unprocessed binaries. 825 interpolating_function <float_type> & operator + (const c2_function<float_type> &rhs) const { 826 return binary_operator(rhs, new c2_sum<float_type>()); } 827 /// \brief produce a newly resampled interpolating_function which is the specified difference. 1532 /// \return a new interpolating_function_p 1533 interpolating_function_p <float_type> & add_pointwise (const c2_function<float_type> &rhs) const { 1534 return binary_operator(rhs, new c2_sum_p<float_type>()); } 1535 /// \brief produce a newly resampled interpolating_function_p which is the specified difference. 828 1536 /// \param rhs the function to subtract, pointwise 829 /// \return a new interpolating_function 830 /// \note 831 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 832 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 833 /// can be upcast back to a c2_function to produce unprocessed binaries. 834 interpolating_function <float_type> & operator - (const c2_function<float_type> &rhs) const { 835 return binary_operator(rhs, new c2_diff<float_type>()); } 836 /// \brief produce a newly resampled interpolating_function which is the specified product. 1537 /// \return a new interpolating_function_p 1538 interpolating_function_p <float_type> & subtract_pointwise (const c2_function<float_type> &rhs) const { 1539 return binary_operator(rhs, new c2_diff_p<float_type>()); } 1540 /// \brief produce a newly resampled interpolating_function_p which is the specified product. 837 1541 /// \param rhs the function to multiply, pointwise 838 /// \return a new interpolating_function 839 /// \note 840 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 841 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 842 /// can be upcast back to a c2_function to produce unprocessed binaries. 843 interpolating_function <float_type> & operator * (const c2_function<float_type> &rhs) const { 844 return binary_operator(rhs, new c2_product<float_type>()); } 845 /// \brief produce a newly resampled interpolating_function which is the specified ratio. 1542 /// \return a new interpolating_function_p 1543 interpolating_function_p <float_type> & multiply_pointwise (const c2_function<float_type> &rhs) const { 1544 return binary_operator(rhs, new c2_product_p<float_type>()); } 1545 /// \brief produce a newly resampled interpolating_function_p which is the specified ratio. 846 1546 /// \param rhs the function to divide, pointwise 847 /// \return a new interpolating_function 848 /// \note 849 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 850 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 851 /// can be upcast back to a c2_function to produce unprocessed binaries. 852 interpolating_function <float_type> & operator / (const c2_function<float_type> &rhs) const { 853 return binary_operator(rhs, new c2_ratio<float_type>()); } 854 /// \brief produce a newly resampled interpolating_function which is the specified sum. 855 /// \param rhs a constant to add, pointwise 856 /// \return a new interpolating_function 857 /// \note 858 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 859 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 860 /// can be upcast back to a c2_function to produce unprocessed binaries. 861 interpolating_function <float_type> & operator + (float_type rhs) const { return (*this)+c2_constant<float_type>(rhs); } 862 /// \brief produce a newly resampled interpolating_function which is the specified difference. 863 /// \param rhs a constant to subtract, pointwise 864 /// \return a new interpolating_function 865 /// \note 866 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 867 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 868 /// can be upcast back to a c2_function to produce unprocessed binaries. 869 interpolating_function <float_type> & operator - (float_type rhs) const { return (*this)-c2_constant<float_type>(rhs); } 870 /// \brief produce a newly resampled interpolating_function which is the specified product. 871 /// \param rhs a constant to multiply, pointwise 872 /// \return a new interpolating_function 873 /// \note 874 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 875 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 876 /// can be upcast back to a c2_function to produce unprocessed binaries. 877 interpolating_function <float_type> & operator * (float_type rhs) const { return (*this)*c2_constant<float_type>(rhs); } 878 /// \brief produce a newly resampled interpolating_function which is the specified ratio. 879 /// \param rhs a constant to divide, pointwise 880 /// \return a new interpolating_function 881 /// \note 882 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table 883 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function 884 /// can be upcast back to a c2_function to produce unprocessed binaries. 885 interpolating_function <float_type> & operator / (float_type rhs) const { return (*this)/c2_constant<float_type>(rhs); } 886 887 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception); 888 889 /// \brief move value & derivatives into our internal coordinates (use splint to go the other way!) 890 /// \note why? 891 void localize_derivatives(float_type xraw, float_type y, float_type yprime, float_type yprime2, float_type *y0, float_type *yp0, float_type *ypp0) const; 892 893 protected: 894 895 interpolating_function() : c2_function<float_type>() { } // default constructor is never used, prevent accidents by protecting it. 896 897 /// \brief do the dirty work of constructing the spline. See interpolating_function constructor for details. 898 void init(const std::vector<float_type> &, const std::vector<float_type> &, 1547 /// \return a new interpolating_function_p 1548 interpolating_function_p <float_type> & divide_pointwise (const c2_function<float_type> &rhs) const { 1549 return binary_operator(rhs, new c2_ratio_p<float_type>()); } 1550 /// \brief copy data from another interpolating function. This only makes sense if the source 1551 /// function has the same transforms as the destination. 1552 /// \param rhs interpolating_function_p to copy from 1553 void clone_data(const interpolating_function_p <float_type> &rhs) { 1554 Xraw=rhs.Xraw; X=rhs.X; F=rhs.F; y2=rhs.y2; 1555 set_sampling_grid_pointer(Xraw); 1556 } 1557 1558 const c2_function_transformation<float_type> &fTransform; 1559 1560 protected: 1561 /// \brief create the spline coefficients 1562 void spline( 899 1563 bool lowerSlopeNatural, float_type lowerSlope, 900 bool upperSlopeNatural, float_type upperSlope, 901 float_type (*inputXConversion)(float_type)=0, 902 float_type (*inputXConversionPrime)(float_type)=0, 903 float_type (*inputXConversionDPrime)(float_type)=0, 904 float_type (*inputYConversion)(float_type)=0, 905 float_type (*inputYConversionPrime)(float_type)=0, 906 float_type (*inputYConversionDPrime)(float_type)=0, 907 float_type (*outputYConversion)(float_type)=0 908 ) throw(c2_exception) ; 1564 bool upperSlopeNatural, float_type upperSlope 1565 ) throw(c2_exception); 1566 1567 // This is for sorting the data. It must be static if it's going to be a class member. 1568 static bool comp_pair(std::pair<float_type,float_type> const &i, std::pair<float_type,float_type> const &j) {return i.first<j.first;} 909 1569 910 1570 std::vector<float_type> Xraw, X, F, y2; 911 912 float_type (*fXin)(float_type), (*fYin)(float_type), (*fYout)(float_type); 913 float_type (*fXinPrime)(float_type), (*fYinPrime)(float_type); 914 float_type (*fXinDPrime)(float_type), (*fYinDPrime)(float_type); 915 916 int xInverted; 917 mutable int lastKLow; 918 }; 919 920 /// \brief An interpolatingFunction with X transformed into log space. 921 /// 1571 c2_const_ptr<float_type> sampler_function; 1572 bool xInverted; 1573 mutable size_t lastKLow; 1574 }; 1575 1576 /// \brief A spline with X transformed into log space. 1577 /// \ingroup interpolators 922 1578 /// Most useful for functions looking like y=log(x) or any other function with a huge X dynamic range, 923 1579 /// and a slowly varying Y. 924 template <typename float_type=double> class log_lin_interpolating_function : public interpolating_function <float_type> { 925 public: 926 /// \brief Construct the function. 927 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing. 928 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid. 929 /// \param f the list of function values. 930 /// \param lowerSlopeNatural if true, set y''(first point)=0 in LogLin space, otherwise compute it from \a lowerSope 931 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false 932 /// \param upperSlopeNatural if true, set y''(last point)=0 in LogLin space, otherwise compute it from \a upperSope 933 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false 934 log_lin_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f, 935 bool lowerSlopeNatural=true, float_type lowerSlope=0.0, 936 bool upperSlopeNatural=true, float_type upperSlope=0.0); 937 protected: 938 log_lin_interpolating_function() {} // do not allow naked construction... it is usually an accident. 939 }; 940 941 942 /// \brief An interpolatingFunction with Y transformed into log space. 943 /// 1580 /// 1581 /// The factory function c2_factory::log_lin_interpolating_function() creates *new log_lin_interpolating_function_p() 1582 template <typename float_type=double> class log_lin_interpolating_function_p : public interpolating_function_p <float_type> { 1583 public: 1584 /// \brief an empty log-linear cubic-spline interpolating_function_p 1585 /// 1586 log_lin_interpolating_function_p() : interpolating_function_p<float_type>(*new c2_log_lin_function_transformation<float_type>) 1587 { } 1588 /// \brief create a new, empty interpolating function of this type (virtual constructor) 1589 virtual interpolating_function_p<float_type> &clone() const throw(c2_exception) 1590 { return *new log_lin_interpolating_function_p<float_type>(); } 1591 }; 1592 1593 1594 /// \brief A spline with Y transformed into log space. 1595 /// \ingroup interpolators 944 1596 /// Most useful for functions looking like y=exp(x) 945 template <typename float_type=double> class lin_log_interpolating_function : public interpolating_function <float_type> { 946 public: 947 /// \brief Construct the function. 948 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing. 949 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid. 950 /// \param f the list of function values. 951 /// \param lowerSlopeNatural if true, set y''(first point)=0 in LinLog space, otherwise compute it from \a lowerSope 952 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false 953 /// \param upperSlopeNatural if true, set y''(last point)=0 in LinLog space, otherwise compute it from \a upperSope 954 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false 955 lin_log_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f, 956 bool lowerSlopeNatural=true, float_type lowerSlope=0.0, 957 bool upperSlopeNatural=true, float_type upperSlope=0.0); 958 protected: 959 lin_log_interpolating_function() {} // do not allow naked construction... it is usually an accident. 960 }; 961 962 963 /// \brief An interpolatingFunction with X and Y transformed into log space. 964 /// 1597 /// 1598 /// The factory function c2_factory::lin_log_interpolating_function() creates *new lin_log_interpolating_function_p() 1599 template <typename float_type=double> class lin_log_interpolating_function_p : public interpolating_function_p <float_type> { 1600 public: 1601 /// \brief an empty linear-log cubic-spline interpolating_function_p 1602 /// 1603 lin_log_interpolating_function_p() : interpolating_function_p<float_type>(*new c2_lin_log_function_transformation<float_type>) 1604 { } 1605 /// \brief create a new, empty interpolating function of this type (virtual constructor) 1606 virtual interpolating_function_p<float_type> &clone() const throw(c2_exception) 1607 { return *new lin_log_interpolating_function_p<float_type>(); } 1608 }; 1609 1610 1611 /// \brief A spline with X and Y transformed into log space. 1612 /// \ingroup interpolators 965 1613 /// Most useful for functions looking like y=x^n or any other function with a huge X and Y dynamic range. 966 template <typename float_type=double> class log_log_interpolating_function : public interpolating_function <float_type> { 967 public: 968 /// \brief Construct the function. 969 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing. 970 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid. 971 /// \param f the list of function values. 972 /// \param lowerSlopeNatural if true, set y''(first point)=0 in LogLog space, otherwise compute it from \a lowerSope 973 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false 974 /// \param upperSlopeNatural if true, set y''(last point)=0 in LogLog space, otherwise compute it from \a upperSope 975 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false 976 log_log_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f, 977 bool lowerSlopeNatural=true, float_type lowerSlope=0.0, 978 bool upperSlopeNatural=true, float_type upperSlope=0.0); 979 protected: 980 log_log_interpolating_function() {} // do not allow naked construction... it is usually an accident. 981 }; 982 983 984 /// \brief An interpolating_function with X in reciprocal space and Y transformed in log space. 985 /// 1614 /// 1615 /// The factory function c2_factory::log_log_interpolating_function() creates *new log_log_interpolating_function_p() 1616 template <typename float_type=double> class log_log_interpolating_function_p : public interpolating_function_p <float_type> { 1617 public: 1618 /// \brief an empty log-log cubic-spline interpolating_function_p 1619 /// 1620 log_log_interpolating_function_p() : interpolating_function_p<float_type>(*new c2_log_log_function_transformation<float_type>) 1621 { } 1622 /// \brief create a new, empty interpolating function of this type (virtual constructor) 1623 virtual interpolating_function_p<float_type> &clone() const throw(c2_exception) 1624 { return *new log_log_interpolating_function_p<float_type>(); } 1625 }; 1626 1627 1628 /// \brief A spline with X in reciprocal space and Y transformed in log space. 1629 /// \ingroup interpolators 986 1630 /// Most useful for thermodynamic types of data where Y is roughly A*exp(-B/x). 987 1631 /// Typical examples are reaction rate data, and thermistor calibration data. 988 template <typename float_type=double> class arrhenius_interpolating_function : public interpolating_function <float_type> { 989 public: 990 /// \brief Construct the function. 991 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing. 992 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid. 993 /// \param f the list of function values. 994 /// \param lowerSlopeNatural if true, set y''(first point)=0 in Arrhenius space, otherwise compute it from \a lowerSope 995 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false 996 /// \param upperSlopeNatural if true, set y''(last point)=0 in Arrhenius space, otherwise compute it from \a upperSope 997 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false 998 arrhenius_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f, 999 bool lowerSlopeNatural=true, float_type lowerSlope=0.0, 1000 bool upperSlopeNatural=true, float_type upperSlope=0.0); 1001 protected: 1002 arrhenius_interpolating_function() {} // do not allow naked construction... it is usually an accident. 1003 }; 1004 1005 /** 1006 \brief create a linear-linear interpolating grid with both x & y set to 1007 (xmin, xmin+dx, ... xmin + (count-1)*dx ) 1008 1009 very useful for transformaiton with other functions e.g. 1010 \code 1011 f=c2_sin<double>::sin(LinearInterpolatingGrid(-0.1,0.1, 65)) 1012 \endcode 1013 creates a spline table of sin(x) slightly beyond the first period 1014 \param xmin the starting point for the grid 1015 \param dx the step size for the grid 1016 \param count the number of points in the grid 1017 \return an identity interpolating_function with the requested grid 1018 */ 1019 template <typename float_type> interpolating_function <float_type> &linear_interpolating_grid(float_type xmin, float_type dx, int count) { 1020 std::vector<float_type> x(count); 1021 for(int i=0; i<count; i++) x[i]=xmin + i * dx; 1022 return *new interpolating_function <float_type>(x,x); 1023 } 1024 1025 /** 1026 \brief create a log-log interpolating grid with both x & y set to 1027 (xmin, xmin*dx, ... xmin * dx^(count-1) ) 1028 1029 very useful for transformaiton with other functions e.g. 1030 \code 1031 f=c2_log<double>::log(LogLogInterpolatingGrid(2, 1.1, 65)) 1032 \endcode 1033 creates a spline table of log(x) 1034 \param xmin the starting point for the grid 1035 \param dx the ratio between points 1036 \param count the number of points in the grid 1037 \return an identity log_log_interpolating_function with the requested grid 1038 */ 1039 template <typename float_type> log_log_interpolating_function <float_type> &log_log_interpolating_grid(float_type xmin, float_type dx, int count) { 1040 std::vector<float_type> x(count); 1041 x[0]=xmin; 1042 for(int i=1; i<count; i++) x[i]=dx*x[i-1]; 1043 return *new log_log_interpolating_function<float_type>(x,x); 1044 } 1632 /// 1633 /// The factory function c2_factory::arrhenius_interpolating_function() creates *new arrhenius_interpolating_function_p() 1634 template <typename float_type=double> class arrhenius_interpolating_function_p : public interpolating_function_p <float_type> { 1635 public: 1636 /// \brief an empty arrhenius cubic-spline interpolating_function_p 1637 /// 1638 arrhenius_interpolating_function_p() : interpolating_function_p<float_type>(*new c2_arrhenius_function_transformation<float_type>) 1639 { } 1640 /// \brief create a new, empty interpolating function of this type (virtual constructor) 1641 virtual interpolating_function_p<float_type> &clone() const throw(c2_exception) 1642 { return *new arrhenius_interpolating_function_p<float_type>(); } 1643 }; 1045 1644 1046 1645 /// \brief compute sin(x) with its derivatives. 1047 /// 1048 /// Creates a singleton instance c2_sin::sin of itself for convenient access. 1049 template <typename float_type=double> class c2_sin : public c2_function<float_type> { 1050 public: 1051 /// \brief constructor. There is alread a singleton c2_sin::sin, which usually suffices. 1052 c2_sin() {} 1646 /// \ingroup math_functions 1647 /// 1648 /// The factory function c2_factory::sin() creates *new c2_sin_p 1649 template <typename float_type=double> class c2_sin_p : public c2_function<float_type> { 1650 public: 1651 /// \brief constructor. 1652 c2_sin_p() : c2_function<float_type>() {} 1053 1653 1054 1654 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) … … 1058 1658 /// \param xmin the lower bound for the grid 1059 1659 /// \param xmax upper bound for the grid 1060 /// \return a new sampling grid. 1061 virtual std::vector<float_type> &get_sampling_grid(float_type xmin, float_type xmax); 1062 /// \brief the static singleton 1063 static const c2_sin sin; 1064 }; 1660 /// \param [in, out] grid the sampling grid. 1661 virtual void get_sampling_grid(float_type xmin, float_type xmax, std::vector<float_type> &grid) const; 1662 }; 1663 1065 1664 /// \brief compute cos(x) with its derivatives. 1066 /// 1067 /// Creates a singleton instance c2_cos::cos of itself for convenient access. 1068 template <typename float_type=double> class c2_cos : public c2_sin<float_type> { 1069 public: 1070 /// \brief constructor. There is already a singleton c2_cos::cos, which usually suffices. 1071 c2_cos() {} 1665 /// \ingroup math_functions 1666 /// 1667 /// The factory function c2_factory::cos() creates *new c2_cos_p 1668 template <typename float_type=double> class c2_cos_p : public c2_sin_p<float_type> { 1669 public: 1670 /// \brief constructor. 1671 c2_cos_p() : c2_sin_p<float_type>() {} 1072 1672 1073 1673 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 1074 1674 { float_type q=std::cos(x); if(yprime) *yprime=-std::sin(x); if(yprime2) *yprime2=-q; return q; } 1075 /// \brief the static singleton 1076 static const c2_cos cos; 1077 }; 1675 }; 1676 1078 1677 /// \brief compute tan(x) with its derivatives. 1079 /// 1080 /// Creates a singleton instance c2_tan::tan of itself for convenient access. 1081 template <typename float_type=double> class c2_tan : public c2_function<float_type> { 1082 public: 1083 /// \brief constructor. There is already a singleton c2_tan::tan, which usually suffices. 1084 c2_tan() {} 1678 /// \ingroup math_functions 1679 /// 1680 /// The factory function c2_factory::tan() creates *new c2_tan_p 1681 template <typename float_type=double> class c2_tan_p : public c2_function<float_type> { 1682 public: 1683 /// \brief constructor. 1684 c2_tan_p() : c2_function<float_type>() {} 1085 1685 1086 1686 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) … … 1092 1692 return t; 1093 1693 } 1094 /// \brief the static singleton 1095 static const c2_tan tan; 1096 }; 1694 }; 1695 1097 1696 /// \brief compute log(x) with its derivatives. 1098 /// 1099 /// Creates a singleton instance c2_log::log of itself for convenient access. 1100 template <typename float_type=double> class c2_log : public c2_function<float_type> { 1101 public: 1102 /// \brief constructor. There is already a singleton c2_log::log, which usually suffices. 1103 c2_log() {} 1697 /// \ingroup math_functions 1698 /// 1699 /// The factory function c2_factory::log() creates *new c2_log_p 1700 template <typename float_type=double> class c2_log_p : public c2_function<float_type> { 1701 public: 1702 /// \brief constructor. 1703 c2_log_p() : c2_function<float_type>() {} 1104 1704 1105 1705 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 1106 1706 { if(yprime) *yprime=1.0/x; if(yprime2) *yprime2=-1.0/(x*x); return std::log(x); } 1107 /// \brief the static singleton 1108 static const c2_log log; 1109 }; 1707 }; 1708 1110 1709 /// \brief compute exp(x) with its derivatives. 1111 /// 1112 /// Creates a singleton instance c2_exp::exp of itself for convenient access. 1113 template <typename float_type=double> class c2_exp : public c2_function<float_type> { 1114 public: 1115 /// \brief constructor. There is already a singleton c2_exp::exp, which usually suffices. 1116 c2_exp() {} 1710 /// \ingroup math_functions 1711 /// 1712 /// The factory function c2_factory::exp() creates *new c2_exp_p 1713 template <typename float_type=double> class c2_exp_p : public c2_function<float_type> { 1714 public: 1715 /// \brief constructor. 1716 c2_exp_p() : c2_function<float_type>() {} 1117 1717 1118 1718 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 1119 1719 { float_type q=std::exp(x); if(yprime) *yprime=q; if(yprime2) *yprime2=q; return q; } 1120 /// \ brief the static singleton 1121 static const c2_exp exp; 1122 }; 1720 }; 1721 1123 1722 /// \brief compute sqrt(x) with its derivatives. 1124 /// 1125 /// Creates a singleton instance c2_sqrt::sqrt of itself for convenient access. 1126 template <typename float_type=double> class c2_sqrt : public c2_function<float_type> { 1127 public: 1128 /// \brief constructor. There is already a singleton c2_sqrt::sqrt, which usually suffices. 1129 c2_sqrt() {} 1723 /// \ingroup math_functions 1724 /// 1725 /// The factory function c2_factory::sqrt() creates *new c2_sqrt_p() 1726 template <typename float_type=double> class c2_sqrt_p : public c2_function<float_type> { 1727 public: 1728 /// \brief constructor. 1729 c2_sqrt_p() : c2_function<float_type>() {} 1130 1730 1131 1731 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 1132 1732 { float_type q=std::sqrt(x); if(yprime) *yprime=0.5/q; if(yprime2) *yprime2=-0.25/(x*q); return q; } 1133 /// \brief the static singleton 1134 static const c2_sqrt sqrt; 1135 }; 1733 }; 1734 1136 1735 /// \brief compute scale/x with its derivatives. 1137 /// 1138 /// Creates a singleton instance c2_recip:recip of itself for convenient access. 1139 template <typename float_type=double> class c2_recip : public c2_function<float_type> { 1140 public: 1141 /// \brief constructor. There is already a singleton c2_recip::recip, which usually suffices. 1142 c2_recip(float_type scale) : rscale(scale) {} 1736 /// \ingroup parametric_functions 1737 /// 1738 /// The factory function c2_factory::recip() creates *new c2_recip_p 1739 template <typename float_type=double> class c2_recip_p : public c2_function<float_type> { 1740 public: 1741 /// \brief constructor. 1742 c2_recip_p(float_type scale) : c2_function<float_type>(), rscale(scale) {} 1143 1743 1144 1744 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) … … 1153 1753 /// \param scale the new numerator 1154 1754 void reset(float_type scale) { rscale=scale; } 1155 /// \brief the static singleton1156 static const c2_recip recip;1157 1755 private: 1158 1756 float_type rscale; 1159 1757 }; 1758 1160 1759 /// \brief compute x with its derivatives. 1161 /// 1162 /// Creates a singleton instance c2_identity::identity of itself for convenient access. 1163 template <typename float_type=double> class c2_identity : public c2_function<float_type> { 1164 public: 1165 /// \brief constructor. There is already a singleton c2_identity::identity, which usually suffices. 1166 c2_identity() {} 1760 /// \ingroup math_functions 1761 /// 1762 /// The factory function c2_factory::identity() creates *new c2_identity_p 1763 template <typename float_type=double> class c2_identity_p : public c2_function<float_type> { 1764 public: 1765 /// \brief constructor. 1766 c2_identity_p() : c2_function<float_type>() {} 1167 1767 1168 1768 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 1169 1769 { if(yprime) *yprime=1.0; if(yprime2) *yprime2=0; return x; } 1170 /// \brief the static singleton1171 static const c2_identity identity;1172 1770 }; 1173 1771 1174 1772 /** 1175 1773 \brief create a linear mapping of another function 1176 1774 \ingroup parametric_functions 1177 1775 for example, given a c2_function \a f 1178 1776 \code 1179 c2_linear<double> L(1.2, 2.0, 3.0); 1180 c2_composed_function<double> &F=L(f); 1777 c2_function<double> &F=c2_linear<double>(1.2, 2.0, 3.0)(f); 1181 1778 \endcode 1182 1779 produces a new c2_function F=2.0+3.0*(\a f - 1.2) 1780 1781 The factory function c2_factory::linear() creates *new c2_linear_p 1183 1782 */ 1184 template <typename float_type=double> class c2_linear : public c2_function<float_type> {1783 template <typename float_type=double> class c2_linear_p : public c2_function<float_type> { 1185 1784 public: 1186 1785 /// \brief Construct the operator f=y0 + slope * (x-x0) … … 1188 1787 /// \param y0 the y-intercept i.e. f(x0) 1189 1788 /// \param slope the slope of the mapping 1190 c2_linear(float_type x0, float_type y0, float_type slope) : xint(x0), intercept(y0), m(slope) {} 1789 c2_linear_p(float_type x0, float_type y0, float_type slope) : 1790 c2_function<float_type>(), xint(x0), intercept(y0), m(slope) {} 1191 1791 /// \brief Change the slope and intercepts after construction. 1192 1792 /// \param x0 the x offset … … 1200 1800 float_type xint, intercept, m; 1201 1801 protected: 1202 c2_linear () {} // do not allow naked construction... it is usually an accident.1802 c2_linear_p() {} // do not allow naked construction... it is usually an accident. 1203 1803 }; 1204 1804 1205 1805 /** 1206 1806 \brief create a quadratic mapping of another function 1207 1807 \ingroup parametric_functions 1208 1808 for example, given a c2_function \a f 1209 1809 \code 1210 c2_quadratic<double> Q(1.2, 2.0, 3.0, 4.0); 1211 c2_composed_function<double> &F=Q(f); 1810 c2_function<double> &F=c2_quadratic<double>(1.2, 2.0, 3.0, 4.0)(f); 1212 1811 \endcode 1213 1812 produces a new c2_function F=2.0 + 3.0*(f-1.2) + 4.0*(f-1.2)^2 … … 1215 1814 note that the parameters are overdetermined, but allows the flexibility of two different representations 1216 1815 1816 The factory function c2_factory::quadratic() creates *new c2_quadratic_p 1217 1817 */ 1218 template <typename float_type=double> class c2_quadratic : public c2_function<float_type> {1818 template <typename float_type=double> class c2_quadratic_p : public c2_function<float_type> { 1219 1819 public: 1220 1820 /// \brief Construct the operator … … 1223 1823 /// \param xcoef the scale on the (\a x - \a x0) term 1224 1824 /// \param x2coef the scale on the (\a x - \a x0)^2 term 1225 c2_quadratic(float_type x0, float_type y0, float_type xcoef, float_type x2coef) : intercept(y0), center(x0), a(x2coef), b(xcoef) {} 1226 /// Modify the mapping after construction 1825 c2_quadratic_p(float_type x0, float_type y0, float_type xcoef, float_type x2coef) : 1826 c2_function<float_type>(), intercept(y0), center(x0), a(x2coef), b(xcoef) {} 1827 /// \brief Modify the coefficients after construction 1227 1828 /// \param x0 the new center around which the powers are computed 1228 1829 /// \param y0 the new value of the function at \a x = \a x0 … … 1236 1837 float_type intercept, center, a, b; 1237 1838 protected: 1238 c2_quadratic () {} // do not allow naked construction... it is usually an accident.1839 c2_quadratic_p() {} // do not allow naked construction... it is usually an accident. 1239 1840 }; 1240 1841 1241 1842 /** 1242 1843 \brief create a power law mapping of another function 1243 1844 \ingroup parametric_functions 1244 1845 for example, given a c2_function \a f 1245 1846 \code 1246 c2_power_law <double> PLaw(1.2, 2.5);1247 c2_composed_function <double> &F=PLaw(f);1847 c2_power_law_p<double> PLaw(1.2, 2.5); 1848 c2_composed_function_p<double> &F=PLaw(f); 1248 1849 \endcode 1249 1850 produces a new c2_function F=1.2 * f^2.5 1250 1851 1852 The factory function c2_factory::power_law() creates *new c2_power_law_p 1251 1853 */ 1252 template <typename float_type=double> class c2_power_law : public c2_function<float_type> {1854 template <typename float_type=double> class c2_power_law_p : public c2_function<float_type> { 1253 1855 public: 1254 1856 /// \brief Construct the operator 1255 1857 /// \param scale the multipler 1256 1858 /// \param power the exponent 1257 c2_power_law(float_type scale, float_type power) : a(scale), b(power) {} 1859 c2_power_law_p(float_type scale, float_type power) : 1860 c2_function<float_type>(), a(scale), b(power) {} 1258 1861 /// \brief Modify the mapping after construction 1259 1862 /// \param scale the new multipler … … 1266 1869 float_type a, b; 1267 1870 protected: 1268 c2_power_law () {} // do not allow naked construction... it is usually an accident.1871 c2_power_law_p() {} // do not allow naked construction... it is usually an accident. 1269 1872 }; 1270 1873 1271 1874 /** 1272 1875 \brief create the formal inverse function of another function 1273 1876 \ingroup containers 1274 1877 for example, given a c2_function \a f 1275 1878 \code … … 1282 1885 derivatives, too, unlike the case of a simple root-finding inverse. This means 1283 1886 it can be integrated (for example) quite efficiently. 1284 1285 Note that it is a subclass of c2_scaled_function only to manage ownership of another c2_function. 1286 */ 1287 template <typename float_type=double> class c2_inverse_function : public c2_plugin_function<float_type> { 1887 1888 \see \ref combined_inversion_hinting_sampling 1889 1890 The factory function c2_factory::inverse_function() creates *new c2_inverse_function_p 1891 */ 1892 template <typename float_type=double> class c2_inverse_function_p : public c2_function<float_type> { 1288 1893 public: 1289 1894 /// \brief Construct the operator 1290 1895 /// \param source the function to be inverted 1291 c2_inverse_function (const c2_function<float_type> &source);1896 c2_inverse_function_p(const c2_function<float_type> &source); 1292 1897 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception); 1293 1898 … … 1301 1906 /// It is used in value_with_derivatives() to guess where to start the root finder. 1302 1907 /// \param x the abscissa for which an estimate is needed 1303 virtual float_type get_start_hint(float_type x) const { return start_hint; } 1304 1908 virtual float_type get_start_hint(float_type x) const 1909 { return hinting_function.valid()? hinting_function(x) : start_hint; } 1910 1911 /// \brief set or unset the approximate function used to start the root finder 1912 /// \anchor set_hinting_function_discussion 1913 /// A hinting function is mostly useful if the evaluation of this inverse is 1914 /// going to be carried out in very non-local order, so the root finder has to start over 1915 /// for each step. If most evaluations are going to be made in fairly localized clusters (scanning 1916 /// through the function, for example), the default mechanism used (which just remembers the last point) 1917 /// is almost certainly faster. 1918 /// 1919 /// Typically, the hinting function is likely to be set up by creating the inverse function, 1920 /// and then adaptively sampling an interpolating function from it, and then using the result 1921 /// to hint it. Another way, if the parent function is already an interpolating function, is just to create 1922 /// a version of the parent with the x & y coordinates reversed. 1923 /// 1924 /// \see \ref combined_inversion_hinting_sampling 1925 /// 1926 /// \param hint_func the function that is an approximate inverse of the parent of this inverse_function 1927 void set_hinting_function(const c2_function<float_type> *hint_func) 1928 { hinting_function.set_function(hint_func); } 1929 /// \brief set the hinting function from a pointer. 1930 /// 1931 /// See \ref set_hinting_function_discussion "discussion" 1932 /// \param hint_func the container holding the function 1933 void set_hinting_function(const c2_const_ptr<float_type> hint_func) 1934 { hinting_function=hint_func; } 1935 1305 1936 protected: 1306 c2_inverse_function() {} // do not allow naked construction... it is usually an accident. 1307 mutable float_type start_hint; 1937 c2_inverse_function_p() {} // do not allow naked construction... it is usually an accident. 1938 mutable float_type start_hint; 1939 const c2_const_ptr<float_type> func; 1940 c2_const_ptr<float_type> hinting_function; 1308 1941 }; 1309 1942 1310 1943 /** 1311 1944 \brief 1312 An interpolating_function which is the cumulative integral of a histogram.1313 1945 An interpolating_function_p which is the cumulative integral of a histogram. 1946 \ingroup interpolators 1314 1947 Note than binedges should be one element longer than binheights, since the lower & upper edges are specified. 1315 1948 Note that this is a malformed spline, since the second derivatives are all zero, so it has less continuity. … … 1318 1951 */ 1319 1952 1320 template <typename float_type=double> class accumulated_histogram : public interpolating_function <float_type> {1953 template <typename float_type=double> class accumulated_histogram : public interpolating_function_p <float_type> { 1321 1954 public: 1322 1955 /// \brief Construct the integrated histogram … … 1332 1965 1333 1966 /** 1334 \brief Construct a function useful for generation of random numbers from the given distribution 1335 1336 inverse_integrated_density<InterpolatingFunctionFlavor>() starts with a probability density c2_function, generates the integral, 1337 and generates an interpolating_function which, when evaluated using a uniform random on [0,1] returns values 1338 with a density distribution equal to the input distribution 1339 If the data are passed in reverse order (large X first), the integral is carried out from the big end. 1967 \anchor inverse_integrated_density_bins 1968 \brief construct from a grid of points and an std::vector of probability densities (un-normalized) 1969 \see \ref random_subsec "Arbitrary random generation" 1970 \ingroup interpolators 1971 inverse_integrated_density starts with a probability density std::vector, generates the integral, 1972 and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values 1973 with a density distribution equal to the input distribution 1974 If the data are passed in reverse order (large X first), the integral is carried out from the big end. 1975 1976 \param bins if \a bins .size()==\a binheights .size(), the centers of the bins. \n 1977 if \a bins .size()==\a binheights .size()+1, the edges of the bins 1978 \param binheights a vector which describes the density of the random number distribution to be produced. 1979 Note density... the numbers in the bins are not counts, but counts/unit bin width. 1980 \return an interpolating_function_p of the type requested in the template which, 1981 if evaluated randomly with a uniform variate on [0,1] produces numbers 1982 distributed according to \a binheights 1983 */ 1984 1985 template <typename float_type, typename Final> 1986 interpolating_function_p<float_type> & inverse_integrated_density_bins( 1987 const std::vector<float_type> &bins, const std::vector<float_type> &binheights) 1988 throw(c2_exception); 1989 1990 /** 1991 \anchor inverse_integrated_density_function 1992 \brief construct from a grid of points and a c2_function of probability densities (un-normalized) 1993 \see \ref random_subsec "Arbitrary random generation" 1994 \ingroup interpolators 1995 inverse_integrated_density starts with a probability density std::vector, generates the integral, 1996 and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values 1997 with a density distribution equal to the input distribution 1998 If the data are passed in reverse order (large X first), the integral is carried out from the big end. 1340 1999 1341 \sa template <typename Intermediate, typename Final> Final inverse_integrated_density(const std::vector, c2_function &) 1342 1343 \param bincenters points at which to sample the c2_function \a binheights 1344 \param binheights a c2_function which describes the random number distribution to be produced. 1345 \return an interpolating_function of the type requested in the template which, 1346 if evaluated randomly with a uniform variate on [0,1) produces numbers 1347 distributed according to \a binheights 1348 */ 1349 1350 template <typename float_type, typename Final > 1351 Final &inverse_integrated_density(const std::vector<float_type> &bincenters, c2_function<float_type> &binheights) 1352 { 1353 std::vector<float_type> integral; 1354 1355 // integrate from first to last bin in original order, leaving results in integral 1356 // ask for relative error of 1e-6 on each bin, with absolute error set to 0 (since we don't know the data scale). 1357 float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6); 1358 // the integral vector now has partial integrals... it must be accumulated by summing 1359 integral.insert(integral.begin(), 0.0); // integral from start to start is 0 1360 float_type scale=1.0/sum; 1361 for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale + integral[i-1]; 1362 integral.back()=1.0; // force exact value on boundary 1363 1364 return *new Final(integral, bincenters, 1365 false, 1.0/(scale*binheights(bincenters.front() )), 1366 false, 1.0/(scale*binheights(bincenters.back() )) 1367 ); // use integral as x axis in inverse function 1368 } 1369 1370 /** 1371 \brief Construct a function useful for generation of random numbers from the given distribution 1372 1373 \code 1374 template <typename Intermediate, typename Final> 1375 Final & inverse_integrated_density(const std::vector &bincenters, const std::vector &binheights) 1376 \endcode 1377 is a variant of \code 1378 template <typename Final> 1379 Final & inverse_integrated_density(const std::vector &bincenters, c2_function &binheights) 1380 \endcode 1381 which takes two std::vectors and generates the intermediate interpolating_function required for 1382 inverse_integrated_density(), and then calls it. 1383 1384 \param bincenters points at which \a binheights are defined 1385 \param binheights an std::vector which describes the random number distribution to be produced. 1386 \return an interpolating_function of the type requested in the template which, 1387 if evaluated randomly with a uniform variate on [0,1) produces numbers 1388 distributed according to \a binheights 1389 */ 1390 1391 template <typename float_type, typename Intermediate, typename Final> Final 1392 &inverse_integrated_density(const std::vector<float_type> &bincenters, const std::vector<float_type> &binheights) 1393 { 1394 std::vector<float_type> be(bincenters), bh(binheights); 1395 1396 if(be[1] < be[0]) { // reverse data for interpolator if x axis passed in backwards 1397 std::reverse(be.begin(), be.end()); 1398 std::reverse(bh.begin(), bh.end()); 1399 } 1400 1401 Intermediate temp(be, bh); // create a temporary interpolating_function to integrate 1402 Final &result=inverse_integrated_density<Final>(bincenters, temp); 1403 1404 return result; 1405 } 2000 \param bincenters the centers of the bins. 2001 \param binheights a c2_function which describes the density of the random number distribution to be produced. 2002 \return an interpolating_function_p of the type requested in the template which, 2003 if evaluated randomly with a uniform variate on [0,1] produces numbers 2004 distributed according to \a binheights 2005 */ 2006 template <typename float_type, typename Final> 2007 interpolating_function_p<float_type> & inverse_integrated_density_function( 2008 const std::vector<float_type> &bincenters, const c2_function<float_type> &binheights) 2009 throw(c2_exception); 1406 2010 1407 2011 /// \brief create a c2_function which smoothly connects two other c2_functions. 1408 /// 2012 /// \ingroup parametric_functions 1409 2013 /// This takes two points and generates a polynomial which matches two c2_function arguments 1410 2014 /// at those two points, with two derivatives at each point, and an arbitrary value at the center of the … … 1414 2018 /// of order 5. If \a auto_center is false, the value \a y1 is used at the midpoint, resulting in a 1415 2019 /// polynomial of order 6. 1416 template <typename float_type=double> class c2_connector_function : public c2_function<float_type> { 2020 /// 2021 /// This is usually used in conjunction with c2_piecewise_function_p to assemble an apparently seamless 2022 /// function from a series of segments. 2023 /// \see \ref piecewise_applications_subsec "Sample Applications" and \ref c2_function::adaptively_sample() "Adaptive sampling" 2024 /// 2025 /// The factory function c2_factory::connector_function() creates *new c2_connector_function_p 2026 template <typename float_type=double> class c2_connector_function_p : public c2_function<float_type> { 1417 2027 public: 1418 /// \brief construct the container 1419 /// \param f1 the function on the left side to be connected 2028 /// \brief construct the container from two functions 2029 /// \param x0 the point at which to match \a f1 and its derivatives 2030 /// \param f0 the function on the left side to be connected 2031 /// \param x2 the point at which to match \a f2 and its derivatives 1420 2032 /// \param f2 the function on the right side to be connected 1421 /// \param x0 the point at which to match \a f1 and its derivatives1422 /// \param x2 the point at which to match \a f2 and its derivatives1423 2033 /// \param auto_center if true, no midpoint value is specified. If false, match the value \a y1 at the midpoint 1424 2034 /// \param y1 the value to match at the midpoint, if \a auto_center is false 1425 /// \return a c2_function with domain (\a x0,\a x2) which smoothly connects \a f 1 and \a f21426 c2_connector_function (const c2_function<float_type> &f1, const c2_function<float_type> &f2, float_type x0, float_type x2,2035 /// \return a c2_function with domain (\a x0,\a x2) which smoothly connects \a f0(x0) and \a f2(x2) 2036 c2_connector_function_p(float_type x0, const c2_function<float_type> &f0, float_type x2, const c2_function<float_type> &f2, 1427 2037 bool auto_center, float_type y1); 2038 /// \brief construct the container from numerical values 2039 /// \param x0 the position of the left edge 2040 /// \param y0 the function derivative on the left boundary 2041 /// \param yp0 the function second derivative on the left boundary 2042 /// \param ypp0 the function value on the left boundary 2043 /// \param x2 the position of the right edge 2044 /// \param y2 the function derivative on the right boundary 2045 /// \param yp2 the function second derivative on the right boundary 2046 /// \param ypp2 the function value on the right boundary 2047 /// \param auto_center if true, no midpoint value is specified. If false, match the value \a y1 at the midpoint 2048 /// \param y1 the value to match at the midpoint, if \a auto_center is false 2049 /// \return a c2_function with domain (\a x0,\a x2) which smoothly connects the points described 2050 /// \anchor c2_connector_raw_init_docs 2051 c2_connector_function_p( 2052 float_type x0, float_type y0, float_type yp0, float_type ypp0, 2053 float_type x2, float_type y2, float_type yp2, float_type ypp2, 2054 bool auto_center, float_type y1); 2055 /// \brief construct the container from c2_fblock<float_type> objects 2056 /// \param fb0 the left edge 2057 /// \param fb2 the right edge 2058 /// \param auto_center if true, no midpoint value is specified. If false, match the value \a y1 at the midpoint 2059 /// \param y1 the value to match at the midpoint, if \a auto_center is false 2060 /// \return a c2_function with domain (\a fb0.x,\a fb2.x) which smoothly connects \a fb0 and \a fb2 2061 c2_connector_function_p( 2062 const c2_fblock<float_type> &fb0, 2063 const c2_fblock<float_type> &fb2, 2064 bool auto_center, float_type y1); 2065 1428 2066 /// \brief destructor 1429 virtual ~c2_connector_function ();2067 virtual ~c2_connector_function_p(); 1430 2068 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception); 1431 2069 protected: 1432 float_type fx1, fhinv, fdx, fy1, fa, fb, fc, fd, fe, ff; 1433 }; 1434 1435 2070 /// \brief fill container numerically 2071 void init( 2072 const c2_fblock<float_type> &fb0, 2073 const c2_fblock<float_type> &fb2, 2074 bool auto_center, float_type y1); 2075 2076 float_type fhinv, fy1, fa, fb, fc, fd, fe, ff; 2077 }; 1436 2078 1437 2079 /// \brief create a c2_function which is a piecewise assembly of other c2_functions. 1438 /// 2080 /// \ingroup containers 1439 2081 /// The functions must have increasing, non-overlapping domains. Any empty space 1440 2082 /// between functions will be filled with a linear interpolation. 2083 /// 2084 /// \note If you want a smooth connection, instead of the default linear interpolation, 2085 /// create a c2_connector_function_p to bridge the gap. The linear interpolation is intended 2086 /// to be a barely intelligent bridge, and may never get used by anyone. 2087 /// 1441 2088 /// \note The creation of the container results in the creation of an explicit sampling grid. 1442 2089 /// If this is used with functions with a large domain, or which generate very dense sampling grids, 1443 2090 /// it could eat a lot of memory. Do not abuse this by using functions which can generate gigantic grids. 1444 2091 /// 1445 /// See c2_plugin_function for a discussion of how this might be used. 1446 template <typename float_type=double> class c2_piecewise_function : public c2_function<float_type> { 2092 /// \see \ref piecewise_applications_subsec "Sample Applications" \n 2093 /// c2_plugin_function_p page \n 2094 /// c2_connector_function_p page \n 2095 /// \ref c2_function::adaptively_sample() "Adaptive sampling" 2096 /// 2097 /// The factory function c2_factory::piecewise_function() creates *new c2_piecewise_function_p 2098 template <typename float_type=double> class c2_piecewise_function_p : public c2_function<float_type> { 1447 2099 public: 1448 2100 /// \brief construct the container 1449 c2_piecewise_function ();2101 c2_piecewise_function_p(); 1450 2102 /// \brief destructor 1451 virtual ~c2_piecewise_function ();2103 virtual ~c2_piecewise_function_p(); 1452 2104 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception); 1453 2105 /// \brief append a new function to the sequence … … 1457 2109 /// the final function. If the domain exactly abuts the domain of the previous function, it 1458 2110 /// will be directly attached. If there is a gap, the gap will be filled in by linear interpolation. 1459 /// If the function being appended is to be deleted automatically when this container is deleted, set the pass_ownership flag.1460 2111 /// \param func a c2_function with a defined domain to be appended to the collection 1461 /// \param pass_ownership if set, \a func will be deleted when the container is destroyed 1462 void append_function(c2_function<float_type> &func, bool pass_ownership) throw (c2_exception); 2112 void append_function(const c2_function<float_type> &func) throw (c2_exception); 1463 2113 protected: 1464 std::vector<c2_function<float_type> *> functions; 1465 std::vector<bool> owns; 2114 std::vector<c2_const_ptr<float_type> > functions; 1466 2115 mutable int lastKLow; 1467 2116 }; -
trunk/examples/extended/electromagnetic/TestEm7/include/c2_function.icc
r807 r1230 1 // 2 // ******************************************************************** 3 // * License and Disclaimer * 4 // * * 5 // * The Geant4 software is copyright of the Copyright Holders of * 6 // * the Geant4 Collaboration. It is provided under the terms and * 7 // * conditions of the Geant4 Software License, included in the file * 8 // * LICENSE and available at http://cern.ch/geant4/license . These * 9 // * include a list of copyright holders. * 10 // * * 11 // * Neither the authors of this software system, nor their employing * 12 // * institutes,nor the agencies providing financial support for this * 13 // * work make any representation or warranty, express or implied, * 14 // * regarding this software system or assume any liability for its * 15 // * use. Please see the license in the file LICENSE and URL above * 16 // * for the full disclaimer and the limitation of liability. * 17 // * * 18 // * This code implementation is the result of the scientific and * 19 // * technical work of the GEANT4 collaboration. * 20 // * By using, copying, modifying or distributing the software (or * 21 // * any work based on the software) you agree to acknowledge its * 22 // * use in resulting scientific publications, and indicate your * 23 // * acceptance of all terms of the Geant4 Software license. * 24 // ******************************************************************** 25 // 26 // 1 27 /** 2 28 * \file … … 7 33 * \author Copyright 2005 __Vanderbilt University__. All rights reserved. 8 34 * 9 * \version c2_function.cc,v 1. 43 2007/11/12 20:22:54marcus Exp35 * \version c2_function.cc,v 1.169 2008/05/22 12:45:19 marcus Exp 10 36 */ 11 37 … … 22 48 23 49 template <typename float_type> const std::string c2_function<float_type>::cvs_file_vers() const 24 { return "c2_function.cc,v 1. 43 2007/11/12 20:22:54marcus Exp"; }50 { return "c2_function.cc,v 1.169 2008/05/22 12:45:19 marcus Exp"; } 25 51 26 52 // find a pre-bracketed root of a c2_function, which is a MUCH easier job than general root finding … … 33 59 // find f(x)=value within the brackets, using the guarantees of smoothness associated with a c2_function 34 60 // can use local f(x)=a*x**2 + b*x + c and solve quadratic to find root, then iterate 35 reset_evaluations();36 61 37 62 float_type yp, yp2; // we will make unused pointers point here, to save null checks later … … 47 72 float_type c, b; 48 73 74 if(!root_info) { 75 root_info=new struct c2_root_info; 76 root_info->inited=false; 77 } 49 78 // this new logic is to keep track of where we were before, and lower the number of 50 79 // function evaluations if we are searching inside the same bracket as before. 51 80 // Since this root finder has, very often, the bracket of the entire domain of the function, 52 81 // this makes a big difference, especially to c2_inverse_function 53 if(!rootInitialized || upper_bracket != lastRootUpperX || lower_bracket != lastRootLowerX) { 54 lastRootUpperY=value_with_derivatives(upper_bracket, final_yprime, final_yprime2); 55 increment_evaluations(); 56 lastRootUpperX=upper_bracket; 57 58 lastRootLowerY=value_with_derivatives(lower_bracket, final_yprime, final_yprime2); 59 increment_evaluations(); 60 lastRootLowerX=lower_bracket; 61 rootInitialized=true; 62 } 63 64 float_type clower=lastRootLowerY-value; 65 float_type cupper=lastRootUpperY-value; 66 if(clower*cupper >0) { 82 if(!root_info->inited || upper_bracket != root_info->upper.x || lower_bracket != root_info->lower.x) { 83 root_info->upper.x=upper_bracket; 84 fill_fblock(root_info->upper); 85 root_info->lower.x=lower_bracket; 86 fill_fblock(root_info->lower); 87 root_info->inited=true; 88 } 89 90 float_type clower=root_info->lower.y-value; 91 if(!clower) { 92 *final_yprime=root_info->lower.yp; 93 *final_yprime2=root_info->lower.ypp; 94 return lower_bracket; 95 } 96 97 float_type cupper=root_info->upper.y-value; 98 if(!cupper) { 99 *final_yprime=root_info->upper.yp; 100 *final_yprime2=root_info->upper.ypp; 101 return upper_bracket; 102 } 103 const float_type lower_sign = (clower < 0) ? -1 : 1; 104 105 if(lower_sign*cupper >0) { 67 106 // argh, no sign change in here! 68 107 if(error) { *error=1; return 0.0; } … … 104 143 } 105 144 c=value_with_derivatives(root, final_yprime, final_yprime2)-value; // compute initial values 145 if(c2_isnan(c)) { 146 bad_x_point=root; 147 return c; // return the nan if a computation failed 148 } 106 149 b=*final_yprime; // make a local copy for readability 107 150 increment_evaluations(); 108 151 109 152 // now, close in bracket on whichever side this still brackets 110 if(c* clower< 0.0) {153 if(c*lower_sign < 0.0) { 111 154 cupper=c; 112 155 upper_bracket=root; … … 131 174 132 175 // the recursive part of the integrator is agressively designed to minimize copying of data... lots of pointers 133 template <typename float_type> float_type c2_function<float_type>::integrate_step(c2_integrate_recur &rb) const 134 { 135 struct c2_integrate_fblock *fbl[3]={rb.f0, rb.f1, rb.f2}; 136 struct c2_integrate_fblock f1; // will hold new middle values 137 float_type retvals[2]={0.0,0.0}; 138 float_type lr[2]; 139 140 // std::cout << "entering with " << rb.f0->x << " " << rb.f1->x << " " << rb.f2->x << std::endl; 141 142 int depth=rb.depth; // save this from the recursion block 143 float_type abs_tol=rb.abs_tol; // this is the value we will pass down 144 float_type *rblr=rb.lr; // save pointer to our parent's lr[2] array since it will get trampled in recursion 145 146 if(!depth) { 176 template <typename float_type> float_type c2_function<float_type>::integrate_step(c2_integrate_recur &rb) const throw(c2_exception) 177 { 178 std::vector< recur_item > &rb_stack=*rb.rb_stack; // heap-based stack of data for recursion 179 rb_stack.clear(); 180 181 recur_item top; 182 top.depth=0; top.done=false; top.f0index=0; top.f2index=0; top.step_sum=0; 183 184 // push storage for our initial elements 185 rb_stack.push_back(top); 186 rb_stack.back().f1=*rb.f0; 187 rb_stack.back().done=true; // this element will never be evaluated further 188 189 rb_stack.push_back(top); 190 rb_stack.back().f1=*rb.f1; 191 rb_stack.back().done=true; // this element will never be evaluated further 192 193 if(!rb.inited) { 147 194 switch(rb.derivs) { 148 195 case 0: … … 157 204 158 205 rb.extrap2=1.0/(rb.extrap_coef-1.0); 159 } 160 161 for (int i=0; i<(depth==0?1:2); i++) { // handle left and right intervals, but only left one for depth=0 162 struct c2_integrate_fblock *f0=fbl[i], *f2=fbl[i+1]; 163 f1.x=0.5*(f0->x + f2->x); // center of interval 164 float_type dx=f2->x - f0->x; 165 float_type dx2 = 0.5*dx; 166 float_type total; 167 168 f1.y=value_with_derivatives(f1.x, &(f1.yp), &(f1.ypp)); 169 increment_evaluations(); 170 206 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon(); 207 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min(); 208 rb.inited=true; 209 } 210 211 // now, push our first real element 212 top.f0index=0; // left element is stack[0] 213 top.f2index=1; // right element is stack[1] 214 top.abs_tol=rb.abs_tol; 215 rb_stack.push_back(top); 216 217 while(rb_stack.size() > 2) { 218 recur_item &back=rb_stack.back(); 219 if(back.done) { 220 float_type sum=back.step_sum; 221 rb_stack.pop_back(); 222 rb_stack.back().step_sum+=sum; // bump our sum up to the parent 223 continue; 224 } 225 back.done=true; 226 227 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, &f2=rb_stack[back.f2index].f1; 228 c2_fblock<float_type> &f1=back.f1; // will hold new middle values 229 size_t f1index=rb_stack.size()-1; // our current offset 230 float_type abs_tol=back.abs_tol; 231 232 f1.x=0.5*(f0.x + f2.x); // center of interval 233 float_type dx2=0.5*(f2.x - f0.x); 234 171 235 // check for underflow on step size, which prevents us from achieving specified accuracy. 172 if(std::abs(dx ) < std::abs(f1.x)*rb.rel_tol) {236 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2) < rb.abs_tol_min) { 173 237 std::ostringstream outstr; 174 outstr << "Step size underflow in adaptive_partial_integrals at depth=" << depth << ", x= " << f1.x;238 outstr << "Step size underflow in adaptive_partial_integrals at depth=" << back.depth << ", x= " << f1.x; 175 239 throw c2_exception(outstr.str().c_str()); 176 240 } 177 241 178 if(!depth) { // top level, total has not been initialized yet 179 switch(rb.derivs) { // create estimate of next lower order for first try 242 fill_fblock(f1); 243 if(c2_isnan(f1.y)) { 244 bad_x_point=f1.x; 245 return f1.y; // can't go any further if a nan has appeared 246 } 247 248 bool yptrouble=f0.ypbad || f2.ypbad || f1.ypbad; 249 bool ypptrouble=f0.yppbad || f2.yppbad || f1.yppbad; 250 251 // select the real derivative count based on whether we are at a point where derivatives exist 252 int derivs = std::min(rb.derivs, (yptrouble||ypptrouble)?(yptrouble?0:1):2); 253 254 if(!back.depth) { // top level, total has not been initialized yet 255 switch(derivs) { // create estimate of next lower order for first try 180 256 case 0: 181 total=0.5*(f0->y+f2->y)*dx; break;257 back.previous_estimate=(f0.y+f2.y)*dx2; break; 182 258 case 1: 183 total=(f0->y+4.0*f1.y+f2->y)*dx/6.0; break;259 back.previous_estimate=(f0.y+4.0*f1.y+f2.y)*dx2/3.0; break; 184 260 case 2: 185 total=( (14*f0->y + 32*f1.y + 14*f2->y) + dx * (f0->yp - f2->yp) ) * dx /60.; break;261 back.previous_estimate=( (14*f0.y + 32*f1.y + 14*f2.y) + 2*dx2 * (f0.yp - f2.yp) ) * dx2 /30.; break; 186 262 default: 187 total=0.0; // just to suppress missing default warnings263 back.previous_estimate=0.0; // just to suppress missing default warnings 188 264 } 189 } else total=rblr[i]; // otherwise, get it from previous level265 } 190 266 191 267 float_type left, right; 192 268 193 switch(rb.derivs) { 269 // pre-compute constants so all multiplies use a small dynamic range 270 // constants for 0 derivative integrator 271 static const float_type c0c1=5./12., c0c2=8./12., c0c3=-1./12.; 272 // constants for 1 derivative integrator 273 static const float_type c1c1=101./240., c1c2=128./240., c1c3=11./240., 274 c1c4=13./240., c1c5=-40./240., c1c6=-3./240.; 275 // constants for 2 derivative integrator 276 static const float_type c2c1=169./40320., c2c2=1024./ 40320., c2c3=-41./40320., 277 c2c4=2727./40320., c2c5=-5040./40320., c2c6=423./40320., 278 c2c7=17007./40320., c2c8=24576./40320., c2c9=-1263./40320.; 279 280 switch(derivs) { 194 281 case 2: 195 282 // use ninth-order estimates for each side, from full set of all values (!) (Thanks, Mathematica!) 196 left= ( ( ( 169*f0->ypp + 1024*f1.ypp - 41*f2->ypp)*dx2 +197 ( 2727*f0->yp - 5040*f1.yp + 423*f2->yp) )*dx2 +198 ( 17007*f0->y + 24576*f1.y - 1263*f2->y) )* (dx2/40320.0);199 right= ( ( ( 169*f2->ypp + 1024*f1.ypp - 41*f0->ypp)*dx2 -200 ( 2727*f2->yp - 5040*f1.yp + 423*f0->yp) )*dx2 +201 ( 17007*f2->y + 24576*f1.y - 1263*f0->y) )* (dx2/40320.0);202 // std::cout << f0 ->x << " " << f1.x << " " << f2->x << std::endl ;203 // std::cout << f0 ->y << " " << f1.y << " " << f2->y << " " << left << " " << right << " " << total << std::endl ;283 left= ( ( (c2c1*f0.ypp + c2c2*f1.ypp + c2c3*f2.ypp)*dx2 + 284 (c2c4*f0.yp + c2c5*f1.yp + c2c6*f2.yp) )*dx2 + 285 (c2c7*f0.y + c2c8*f1.y + c2c9*f2.y) )* dx2; 286 right= ( ( (c2c1*f2.ypp + c2c2*f1.ypp + c2c3*f0.ypp)*dx2 - 287 (c2c4*f2.yp + c2c5*f1.yp + c2c6*f0.yp) )*dx2 + 288 (c2c7*f2.y + c2c8*f1.y + c2c9*f0.y) )* dx2; 289 // std::cout << f0.x << " " << f1.x << " " << f2.x << std::endl ; 290 // std::cout << f0.y << " " << f1.y << " " << f2.y << " " << left << " " << right << " " << total << std::endl ; 204 291 break; 205 292 case 1: 206 left= ( ( 202*f0->y + 256*f1.y + 22*f2->y) + dx*(13*f0->yp - 40*f1.yp - 3*f2->yp) ) * dx /960.;207 right= ( ( 202*f2->y + 256*f1.y + 22*f0->y) - dx*(13*f2->yp - 40*f1.yp - 3*f0->yp) ) * dx /960.;293 left= ( (c1c1*f0.y + c1c2*f1.y + c1c3*f2.y) + dx2*(c1c4*f0.yp + c1c5*f1.yp + c1c6*f2.yp) ) * dx2 ; 294 right= ( (c1c1*f2.y + c1c2*f1.y + c1c3*f0.y) - dx2*(c1c4*f2.yp + c1c5*f1.yp + c1c6*f0.yp) ) * dx2 ; 208 295 break; 209 296 case 0: 210 left= ( 5*f0->y + 8*f1.y - f2->y)*dx/24.;211 right= ( 5*f2->y + 8*f1.y - f0->y)*dx/24.;297 left= (c0c1*f0.y + c0c2*f1.y + c0c3*f2.y)*dx2; 298 right= (c0c1*f2.y + c0c2*f1.y + c0c3*f0.y)*dx2; 212 299 break; 213 300 default: … … 216 303 } 217 304 218 lr[0]= left; // left interval219 lr[1]= right; // right interval220 305 float_type lrsum=left+right; 221 306 222 float_type eps=std::abs(total-lrsum)*rb.eps_scale; 223 if(rb.extrapolate) eps*=rb.eps_scale; 224 225 if(!rb.adapt || eps < abs_tol || eps < std::abs(total)*rb.rel_tol) { 226 if(depth==0 || !rb.extrapolate) retvals[i]=lrsum; 227 else { 228 retvals[i]=(rb.extrap_coef*lrsum - total)*rb.extrap2; 229 // std::cout << "extrapolating " << lrsum << " " << total << " " << retvals[i] << std::endl; 230 231 } 307 bool extrapolate=back.depth && rb.extrapolate && (derivs==rb.derivs); // only extrapolate if no trouble with derivs 308 float_type eps=std::abs(back.previous_estimate-lrsum)*rb.eps_scale; 309 if(extrapolate) eps*=rb.eps_scale; 310 311 if(rb.adapt && eps > abs_tol && eps > std::abs(lrsum)*rb.rel_tol) { 312 // tolerance not met, subdivide & recur 313 if(abs_tol > rb.abs_tol_min) abs_tol=abs_tol*0.5; // each half has half the error budget 314 top.abs_tol=abs_tol; 315 top.depth=back.depth+1; 316 317 // save the last things we need from back before a push happens, in case 318 // the push causes a reallocation and moves the whole stack. 319 size_t f0index=back.f0index, f2index=back.f2index; 320 321 top.f0index=f1index; top.f2index=f2index; // insert pointers to right side data into our recursion block 322 top.previous_estimate=right; 323 rb_stack.push_back(top); 324 325 top.f0index=f0index; top.f2index=f1index; // insert pointers to left side data into our recursion block 326 top.previous_estimate=left; 327 rb_stack.push_back(top); 328 329 } else if(extrapolate) { 330 // extrapolation only happens on leaf nodes, where the tolerance was met. 331 back.step_sum+=(rb.extrap_coef*lrsum - back.previous_estimate)*rb.extrap2; 232 332 } else { 233 rb.depth=depth+1; // increment depth counter 234 rb.lr=lr; // point to our left-right values array for recursion 235 rb.abs_tol=abs_tol*0.5; // each half has half the error budget 236 rb.f0=f0; rb.f1=&f1; rb.f2=f2; // insert pointers to data into our recursion block 237 // std::cout << "recurring with " << f0->x << " " << f1.x << " " << f2->x << std::endl ; 238 retvals[i]=integrate_step(rb); // and recur 239 } 240 } 241 return retvals[0]+retvals[1]; 333 back.step_sum+=lrsum; 334 } 335 } 336 return rb_stack.back().step_sum; // last element on the stack holds the sum 242 337 } 243 338 244 339 template <typename float_type> bool c2_function<float_type>::check_monotonicity( 245 const std::vector<float_type> &data, const char message[]) throw(c2_exception)340 const std::vector<float_type> &data, const char message[]) const throw(c2_exception) 246 341 { 247 342 size_t np=data.size(); … … 269 364 } 270 365 271 template <typename float_type> std::vector<float_type> &c2_function<float_type>::get_sampling_grid(float_type xmin, float_type xmax) const 272 { 273 std::vector<float_type> *result=new std::vector<float_type>; 366 template <typename float_type> void c2_function<float_type>:: 367 get_sampling_grid(float_type xmin, float_type xmax, std::vector<float_type> &grid) const 368 { 369 std::vector<float_type> *result=&grid; 370 result->clear(); 274 371 275 372 if( !(sampling_grid) || !(sampling_grid->size()) || (xmax <= sampling_grid->front()) || (xmin >= sampling_grid->back()) ) { … … 313 410 std::copy(sg.begin()+firstindex, sg.begin()+lastindex+1, result->begin()+initsize); 314 411 result->back()=xmax; 315 412 316 413 // this is the unrefined sampling grid... now check for very close points on front & back and fix if needed. 317 414 preen_sampling_grid(result); 318 415 } 319 return *result;320 416 } 321 417 … … 351 447 } 352 448 353 template <typename float_type> std::vector<float_type> &c2_function<float_type>::354 refine_sampling_grid( conststd::vector<float_type> &grid, size_t refinement) const449 template <typename float_type> void c2_function<float_type>:: 450 refine_sampling_grid(std::vector<float_type> &grid, size_t refinement) const 355 451 { 356 452 size_t np=grid.size(); … … 358 454 float_type dxscale=1.0/refinement; 359 455 360 std::vector<float_type> *result=new std::vector<float_type>(count);456 std::vector<float_type> result(count); 361 457 362 458 for(size_t i=0; i<(np-1); i++) { 363 459 float_type x=grid[i]; 364 460 float_type dx=(grid[i+1]-x)*dxscale; 365 for(size_t j=0; j<refinement; j++, x+=dx) (*result)[i*refinement+j]=x;366 } 367 (*result)[count-1]=grid.back();368 return *result;461 for(size_t j=0; j<refinement; j++, x+=dx) result[i*refinement+j]=x; 462 } 463 result.back()=grid.back(); 464 grid=result; // copy the expanded grid back to the input 369 465 } 370 466 371 467 template <typename float_type> float_type c2_function<float_type>::integral(float_type xmin, float_type xmax, std::vector<float_type> *partials, 372 float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate) const 373 { 374 std::vector<float_type> &grid=get_sampling_grid(xmin, xmax); 375 float_type intg=partial_integrals(grid, partials, abs_tol, rel_tol, adapt, extrapolate); 376 delete &grid; 468 float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate) const throw(c2_exception) 469 { 470 if(xmin==xmax) { 471 if(partials) partials->clear(); 472 return 0.0; 473 } 474 std::vector<float_type> grid; 475 get_sampling_grid(xmin, xmax, grid); 476 float_type intg=partial_integrals(grid, partials, abs_tol, rel_tol, derivs, adapt, extrapolate); 377 477 return intg; 378 478 } 379 479 380 480 template <typename float_type> c2_function<float_type> &c2_function<float_type>::normalized_function(float_type xmin, float_type xmax, float_type norm) 481 const throw(c2_exception) 381 482 { 382 483 float_type intg=integral(xmin, xmax); 383 return *new c2_scaled_function<float_type>(*this, norm/intg); 384 } 385 386 template <typename float_type> c2_function<float_type> &c2_function<float_type>::square_normalized_function(float_type xmin, float_type xmax, float_type norm) 387 { 388 c2_quadratic<float_type> q(0., 0., 0., 1.); 389 c2_composed_function<float_type> mesquared(q,*this); 390 391 std::vector<float_type> grid(get_sampling_grid(xmin, xmax)); 392 float_type intg=mesquared.partial_integrals(grid); 393 394 return *new c2_scaled_function<float_type>(*this, std::sqrt(norm/intg)); 484 return *new c2_scaled_function_p<float_type>(*this, norm/intg); 485 } 486 487 template <typename float_type> c2_function<float_type> &c2_function<float_type>::square_normalized_function(float_type xmin, float_type xmax, float_type norm) 488 const throw(c2_exception) 489 { 490 c2_ptr<float_type> mesquared((*new c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this)); 491 492 std::vector<float_type> grid; 493 get_sampling_grid(xmin, xmax, grid); 494 float_type intg=mesquared->partial_integrals(grid); 495 496 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg)); 395 497 } 396 498 397 499 template <typename float_type> c2_function<float_type> &c2_function<float_type>::square_normalized_function( 398 500 float_type xmin, float_type xmax, const c2_function<float_type> &weight, float_type norm) 399 { 400 c2_quadratic<float_type> q(0., 0., 0., 1.); 401 c2_ composed_function<float_type> mesquared(q,*this);402 c2_product<float_type> weighted(mesquared, weight); 403 404 std::vector<float_type> grid(get_sampling_grid(xmin, xmax));405 float_type intg=weighted .partial_integrals(grid);406 407 return *new c2_scaled_function <float_type>(*this, std::sqrt(norm/intg));501 const throw(c2_exception) 502 { 503 c2_ptr<float_type> weighted((*new c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this) * weight); 504 505 std::vector<float_type> grid; 506 get_sampling_grid(xmin, xmax, grid); 507 float_type intg=weighted->partial_integrals(grid); 508 509 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg)); 408 510 } 409 511 410 512 template <typename float_type> float_type c2_function<float_type>::partial_integrals( 411 513 std::vector<float_type> xgrid, std::vector<float_type> *partials, 412 float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate) const 514 float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate) 515 const throw(c2_exception) 413 516 { 414 517 int np=xgrid.size(); 415 518 416 struct c2_integrate_fblockf0, f2;519 c2_fblock<float_type> f0, f2; 417 520 struct c2_integrate_recur rb; 418 521 rb.rel_tol=rel_tol; … … 420 523 rb.adapt=adapt; 421 524 rb.derivs=derivs; 422 423 reset_evaluations(); // counter returns with total evaluations needed for this integral 525 std::vector< recur_item > rb_stack; 526 rb_stack.reserve(20); // enough for most operations 527 rb.rb_stack=&rb_stack; 528 rb.inited=false; 529 float_type dx_inv=1.0/std::abs(xgrid.back()-xgrid.front()); 424 530 425 531 if(partials) partials->resize(np-1); … … 428 534 429 535 f2.x=xgrid[0]; 430 f2.y=value_with_derivatives(f2.x, &f2.yp, &f2.ypp); 431 increment_evaluations(); 536 fill_fblock(f2); 537 if(c2_isnan(f2.y)) { 538 bad_x_point=f2.x; 539 return f2.y; // can't go any further if a nan has appeared 540 } 432 541 433 542 for(int i=0; i<np-1; i++) { … … 435 544 436 545 f2.x=xgrid[i+1]; 437 f2.y=value_with_derivatives(f2.x, &f2.yp, &f2.ypp); 438 increment_evaluations(); 439 440 rb.depth=0; 441 rb.abs_tol=abs_tol; 442 rb.f0=&f0; rb.f1=&f2; rb.f2=&f2; // we are really only using the left half for the top level 443 rb.lr=0; // pointer is meaningless; will be filled in in recursion 546 fill_fblock(f2); 547 if(c2_isnan(f2.y)) { 548 bad_x_point=f2.x; 549 return f2.y; // can't go any further if a nan has appeared 550 } 551 552 rb.abs_tol=abs_tol*std::abs(f2.x-f0.x)*dx_inv; // distribute error tolerance over whole domain 553 rb.f0=&f0; rb.f1=&f2; 444 554 float_type ps=integrate_step(rb); 445 555 sum+=ps; 446 556 if(partials) (*partials)[i]=ps; 557 if(c2_isnan(ps)) break; // NaN stops integration 447 558 } 448 559 return sum; 449 560 } 450 451 // declare singleton functions for most common c2_function instances452 #define c2_singleton(X) template <typename float_type> const c2_##X<float_type> c2_##X<float_type>::X=c2_##X();453 c2_singleton(sin)454 c2_singleton(cos)455 c2_singleton(tan)456 c2_singleton(log)457 c2_singleton(exp)458 c2_singleton(sqrt)459 c2_singleton(identity)460 461 // reciprocal is actually parametric (a/x), but make singleton 1/x462 template <typename float_type> const c2_recip<float_type> c2_recip<float_type>::recip=c2_recip(1.0);463 464 #undef c2_singleton465 561 466 562 // generate a sampling grid at points separated by dx=5, which is intentionally 467 563 // incommensurate with pi and 2*pi so grid errors are somewhat randomized 468 template <typename float_type> std::vector<float_type> &c2_sin<float_type>::get_sampling_grid(float_type xmin, float_type xmax) 469 { 470 std::vector<float_type> *result=new std::vector<float_type>; 471 472 for(; xmin < xmax; xmin+=5.0) result->push_back(xmin); 473 result->push_back(xmax); 474 this->preen_sampling_grid(result); 475 return *result; 476 } 477 478 template <typename float_type> float_type Identity(float_type x) { return x; } // a useful function 479 template <typename float_type> float_type f_one(float_type) { return 1.0; } // the first derivative of identity 480 template <typename float_type> float_type f_zero(float_type) { return 0.0; } // the second derivative of identity 564 template <typename float_type> void c2_sin_p<float_type>:: 565 get_sampling_grid(float_type xmin, float_type xmax, std::vector<float_type> &grid) const 566 { 567 grid.clear(); 568 for(; xmin < xmax; xmin+=5.0) grid.push_back(xmin); 569 grid.push_back(xmax); 570 this->preen_sampling_grid(&grid); 571 } 572 573 template <typename float_type> float_type c2_function_transformation<float_type>::evaluate( 574 float_type xraw, 575 float_type y, float_type yp0, float_type ypp0, 576 float_type *yprime, float_type *yprime2) const 577 { 578 y=Y.fHasStaticTransforms ? Y.pOut(y) : Y.fOut(y); 579 580 if(yprime || yprime2) { 581 582 float_type yp, yp2; 583 if(X.fHasStaticTransforms && Y.fHasStaticTransforms) { 584 float_type fpi=1.0/Y.pInPrime(y); 585 float_type gp=X.pInPrime(xraw); 586 // from Mathematica Dt[InverseFunction[f][y[g[x]]], x] 587 yp=gp*yp0*fpi; // transformed derivative 588 yp2=(gp*gp*ypp0 + X.pInDPrime(xraw)*yp0 - Y.pInDPrime(y)*yp*yp )*fpi; 589 } else { 590 float_type fpi=1.0/Y.fInPrime(y); 591 float_type gp=X.fInPrime(xraw); 592 // from Mathematica Dt[InverseFunction[f][y[g[x]]], x] 593 yp=gp*yp0*fpi; // transformed derivative 594 yp2=(gp*gp*ypp0 + X.fInDPrime(xraw)*yp0 - Y.fInDPrime(y)*yp*yp )*fpi; 595 } 596 if(yprime) *yprime=yp; 597 if(yprime2) *yprime2=yp2; 598 } 599 return y; 600 } 481 601 482 602 // The constructor 483 template <typename float_type> void interpolating_function<float_type>::init(603 template <typename float_type> interpolating_function_p<float_type> & interpolating_function_p<float_type>::load( 484 604 const std::vector<float_type> &x, const std::vector<float_type> &f, 485 605 bool lowerSlopeNatural, float_type lowerSlope, 486 606 bool upperSlopeNatural, float_type upperSlope, 487 float_type (*inputXConversion)(float_type), 488 float_type (*inputXConversionPrime)(float_type), 489 float_type (*inputXConversionDPrime)(float_type), 490 float_type (*inputYConversion)(float_type), 491 float_type (*inputYConversionPrime)(float_type), 492 float_type (*inputYConversionDPrime)(float_type), 493 float_type (*outputYConversion)(float_type) 494 ) throw(c2_exception) 495 { 607 bool splined 608 ) throw(c2_exception) 609 { 610 c2_ptr<float_type> keepme(*this); 496 611 X= x; 497 612 F= f; … … 501 616 502 617 set_domain(std::min(Xraw.front(), Xraw.back()),std::max(Xraw.front(), Xraw.back())); 503 504 fXin=inputXConversion; 505 fXinPrime=inputXConversionPrime; 506 fXinDPrime=inputXConversionDPrime; 507 fYin=inputYConversion; 508 fYinPrime=inputYConversionPrime; 509 fYinDPrime=inputYConversionDPrime; 510 fYout=outputYConversion; 511 618 512 619 if(x.size() != f.size()) { 513 620 throw c2_exception("interpolating_function::init() -- x & y inputs are of different size"); … … 529 636 } 530 637 531 if(fXin) { // check if X scale is nonlinear, and if so, do transform 532 if(!lowerSlopeNatural) lowerSlope /= fXinPrime(X[0]); 533 if(!upperSlopeNatural) upperSlope /= fXinPrime(X[np-1]); 534 for(size_t i=0; i<np; i++) X[i]=fXin(X[i]); 535 } else { 536 fXin=Identity<float_type>; 537 fXinPrime=f_one<float_type>; 538 fXinDPrime=f_zero<float_type>; 539 } 540 541 if(inputYConversion) { // check if Y scale is nonlinear, and if so, do transform 542 if(!lowerSlopeNatural) lowerSlope *= fYinPrime(F[0]); 543 if(!upperSlopeNatural) upperSlope *= fYinPrime(F[np-1]); 544 for(size_t i=0; i<np; i++) F[i]=inputYConversion(F[i]); 545 } else { 546 fYin=Identity<float_type>; 547 fYinPrime=f_one<float_type>; 548 fYinDPrime=f_zero<float_type>; 549 fYout=Identity<float_type>; 638 if(fTransform.X.fTransformed) { // check if X scale is nonlinear, and if so, do transform 639 if(!lowerSlopeNatural) lowerSlope /= fTransform.X.fInPrime(X[0]); 640 if(!upperSlopeNatural) upperSlope /= fTransform.X.fInPrime(X[np-1]); 641 for(size_t i=0; i<np; i++) X[i]=fTransform.X.fIn(X[i]); 642 } 643 if(fTransform.Y.fTransformed) { // check if Y scale is nonlinear, and if so, do transform 644 if(!lowerSlopeNatural) lowerSlope *= fTransform.Y.fInPrime(F[0]); 645 if(!upperSlopeNatural) upperSlope *= fTransform.Y.fInPrime(F[np-1]); 646 for(size_t i=0; i<np; i++) F[i]=fTransform.Y.fIn(F[i]); 550 647 } 551 648 … … 553 650 "interpolating_function::init() non-monotonic transformed x input"); 554 651 555 // construct spline tables here. 652 if(splined) spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope); 653 else y2.assign(np,0.0); 654 655 lastKLow=0; 656 keepme.release_for_return(); 657 return *this; 658 } 659 660 /* 661 // The constructor 662 template <typename float_type> interpolating_function_p<float_type> & interpolating_function_p<float_type>::load_pairs( 663 std::vector<std::pair<float_type, float_type> > &data, 664 bool lowerSlopeNatural, float_type lowerSlope, 665 bool upperSlopeNatural, float_type upperSlope, 666 bool splined 667 ) throw(c2_exception) 668 { 669 c2_ptr<float_type> keepme(*this); 670 671 size_t np=data.size(); 672 if(np < 2) { 673 throw c2_exception("interpolating_function::init() -- input < 2 elements "); 674 } 675 676 // sort into ascending order 677 std::sort(data.begin(), data.end(), comp_pair); 678 679 std::vector<float_type> xtmp, ytmp; 680 xtmp.reserve(np); 681 ytmp.reserve(np); 682 for (size_t i=0; i<np; i++) { 683 xtmp.push_back(data[i].first); 684 ytmp.push_back(data[i].second); 685 } 686 this->load(xtmp, ytmp, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, splined); 687 688 keepme.release_for_return(); 689 return *this; 690 } 691 692 template <typename float_type> interpolating_function_p<float_type> & 693 interpolating_function_p<float_type>::load_random_generator_function( 694 const std::vector<float_type> &bincenters, const c2_function<float_type> &binheights) 695 throw(c2_exception) 696 { 697 c2_ptr<float_type> keepme(*this); 698 699 std::vector<float_type> integral; 700 c2_const_ptr<float_type> keepit(binheights); // manage function... not really needed here, but always safe. 701 // integrate from first to last bin in original order, leaving results in integral 702 // ask for relative error of 1e-6 on each bin, with absolute error set to 0 (since we don't know the data scale). 703 float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6); 704 // the integral vector now has partial integrals... it must be accumulated by summing 705 integral.insert(integral.begin(), 0.0); // integral from start to start is 0 706 float_type scale=1.0/sum; 707 for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale + integral[i-1]; 708 integral.back()=1.0; // force exact value on boundary 709 710 this->load(integral, bincenters, 711 false, 1.0/(scale*binheights(bincenters.front() )), 712 false, 1.0/(scale*binheights(bincenters.back() )) 713 ); // use integral as x axis in inverse function 714 keepme.release_for_return(); 715 return *this; 716 } 717 718 template <typename float_type> interpolating_function_p<float_type> & 719 interpolating_function_p<float_type>::load_random_generator_bins( 720 const std::vector<float_type> &bins, const std::vector<float_type> &binheights) 721 throw(c2_exception) 722 { 723 c2_ptr<float_type> keepme(*this); 724 725 size_t np=binheights.size(); 726 std::vector<float_type> integral(np+1), bin_edges(np+1); 727 728 // compute the integral based on estimates of the bin edges from the given bin centers... 729 // except for bin 0 & final bin, the edge of a bin is halfway between then center of the 730 // bin and the center of the previous/next bin. 731 // This gives width[n] = (center[n+1]+center[n])/2 - (center[n]+center[n-1])/2 = (center[n+1]-center[n-1])/2 732 // for the edges, assume a bin of width (center[1]-center[0]) or (center[np-1]-center[np-2]) 733 // be careful that absolute values are used in case data are reversed. 734 735 if(bins.size() == binheights.size()+1) { 736 bin_edges=bins; // edges array was passed in 737 } else if (bins.size() == binheights.size()) { 738 bin_edges.front()=bins[0] - (bins[1]-bins[0])*0.5; // edge bin 739 for(size_t i=1; i<np; i++) { 740 bin_edges[i]=(bins[i]+bins[i-1])*0.5; 741 } 742 bin_edges.back()=bins[np-1] + (bins[np-1]-bins[np-2])*0.5; // edge bin 743 } else { 744 throw c2_exception("inconsistent bin vectors passed to load_random_generator_bins"); 745 } 746 747 float_type running_sum=0.0; 748 for(size_t i=0; i<np; i++) { 749 integral[i]=running_sum; 750 if(!binheights[i]) throw c2_exception("empty bin passed to load_random_generator_bins"); 751 running_sum+=binheights[i]*std::abs(bin_edges[i+1]-bin_edges[i]); 752 } 753 float_type scale=1.0/running_sum; 754 for(size_t i=0; i<np; i++) integral[i]*=scale; 755 integral.back()=1.0; // force exactly correct value on boundary 756 this->load(integral, bin_edges, 757 false, 1.0/(scale*binheights.front()), 758 false, 1.0/(scale*binheights.back()) 759 ); // use integral as x axis in inverse function 760 keepme.release_for_return(); 761 return *this; 762 } 763 */ 764 765 // The spline table generator 766 template <typename float_type> void interpolating_function_p<float_type>::spline( 767 bool lowerSlopeNatural, float_type lowerSlope, 768 bool upperSlopeNatural, float_type upperSlope 769 ) throw(c2_exception) 770 { 771 // construct spline tables here. 556 772 // this code is a re-translation of the pythonlabtools spline algorithm from pythonlabtools.sourceforge.net 557 773 size_t np=X.size(); 558 774 std::vector<float_type> u(np), dy(np-1), dx(np-1), dxi(np-1), dx2i(np-2), siga(np-2), dydx(np-1); 559 775 … … 596 812 y2[np-1]=(un-qn*u[np-2])/(qn*y2[np-2]+1.0); 597 813 for (size_t k=np-1; k != 0; k--) y2[k-1]=y2[k-1]*y2[k]+u[k-1]; 598 599 lastKLow=-1; // flag new X search required for next evaluation 814 } 815 816 template <typename float_type> interpolating_function_p<float_type> &interpolating_function_p<float_type>::sample_function( 817 const c2_function<float_type> &func, 818 float_type xmin, float_type xmax, float_type abs_tol, float_type rel_tol, 819 bool lowerSlopeNatural, float_type lowerSlope, 820 bool upperSlopeNatural, float_type upperSlope 821 ) throw(c2_exception) 822 { 823 c2_ptr<float_type> keepme(*this); 824 825 const c2_transformation<float_type> &XX=fTransform.X, &YY=fTransform.Y; // shortcuts 826 827 // set up our params to look like the samplng function for now 828 sampler_function=func; 829 std::vector<float_type> grid; 830 func.get_sampling_grid(xmin, xmax, grid); 831 size_t gsize=grid.size(); 832 if(XX.fTransformed) for(size_t i=0; i<gsize; i++) grid[i]=XX.fIn(grid[i]); 833 set_sampling_grid_pointer(grid); 834 835 // float_type xmin1=fXin(xmin), xmax1=fXin(xmax); // bounds in transformed space 836 // get a list of points needed in transformed space, directly into our tables 837 this->adaptively_sample(grid.front(), grid.back(), 8*abs_tol, 8*rel_tol, 0, &X, &F); 838 // clear the sampler function now, since otherwise our value_with_derivatives is broken 839 sampler_function.unset_function(); 840 841 xInverted=check_monotonicity(X, 842 "interpolating_function::init() non-monotonic transformed x input"); 843 844 size_t np=X.size(); 845 846 // Xraw is useful in some of the arithmetic operations between interpolating functions 847 if(!XX.fTransformed) Xraw=X; 848 else { 849 Xraw.resize(np); 850 for (size_t i=1; i<np-1; i++) Xraw[i]=XX.fOut(X[i]); 851 Xraw.front()=xmin; 852 Xraw.back()=xmax; 853 } 854 855 bool xraw_rev=check_monotonicity(Xraw, 856 "interpolating_function::init() non-monotonic raw x input"); 857 // which way does raw X point? sampling grid MUST be increasing 858 859 if(!xraw_rev) { // we can use pointer to raw X values if they are in the right order 860 set_sampling_grid_pointer(Xraw); 861 // our intial grid of x values is certainly a good guess for 'interesting' points 862 } else { 863 set_sampling_grid(Xraw); // make a copy of it, and assure it is in right order 864 } 865 866 if(XX.fTransformed) { // check if X scale is nonlinear, and if so, do transform 867 if(!lowerSlopeNatural) lowerSlope /= XX.fInPrime(xmin); 868 if(!upperSlopeNatural) upperSlope /= XX.fInPrime(xmax); 869 } 870 if(YY.fTransformed) { // check if Y scale is nonlinear, and if so, do transform 871 if(!lowerSlopeNatural) lowerSlope *= YY.fInPrime(func(xmin)); 872 if(!upperSlopeNatural) upperSlope *= YY.fInPrime(func(xmax)); 873 } 874 // note that each of ends has 3 points with two equal gaps, since they were obtained by bisection 875 // so the step sizes are easy to get 876 // the 'natural slope' option for sampled functions has a different meaning than 877 // for normal splines. In this case, the derivative is adjusted to make the 878 // second derivative constant on the last two points at each end 879 // which is consistent with the error sampling technique we used to get here 880 if(lowerSlopeNatural) { 881 float_type hlower=X[1]-X[0]; 882 lowerSlope=0.5*(-F[2]-3*F[0]+4*F[1])/hlower; 883 lowerSlopeNatural=false; // it's not the usual meaning of natural any more 884 } 885 if(upperSlopeNatural) { 886 float_type hupper=X[np-1]-X[np-2]; 887 upperSlope=0.5*(F[np-3]+3*F[np-1]-4*F[np-2])/hupper; 888 upperSlopeNatural=false; // it's not the usual meaning of natural any more 889 } 890 this->set_domain(xmin, xmax); 891 892 spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope); 893 lastKLow=0; 894 keepme.release_for_return(); 895 return *this; 600 896 } 601 897 602 898 // This function is the reason for this class to exist 603 899 // it computes the interpolated function, and (if requested) its proper first and second derivatives including all coordinate transforms 604 template <typename float_type> float_type interpolating_function <float_type>::value_with_derivatives(900 template <typename float_type> float_type interpolating_function_p<float_type>::value_with_derivatives( 605 901 float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) 606 902 { 903 if(sampler_function.valid()) { 904 // if this is non-null, we are sampling data for later, so just return raw function 905 // however, transform it into our sampling space, first. 906 if(yprime) *yprime=0; 907 if(yprime2) *yprime2=0; 908 sampler_function->increment_evaluations(); 909 return fTransform.Y.fIn(sampler_function(fTransform.X.fOut(x))); // derivatives are completely undefined 910 } 911 607 912 if(x < this->xmin() || x > this->xmax()) { 608 913 std::ostringstream outstr; … … 613 918 float_type xraw=x; 614 919 615 // template here is impossible! if(fXin && fXin != (Identity<float_type>) )616 x=fXin(x); // save time by explicitly testing for identity function here920 if(fTransform.X.fTransformed) x=fTransform.X.fHasStaticTransforms? 921 fTransform.X.pIn(x) : fTransform.X.fIn(x); // save time by explicitly testing for identity function here 617 922 618 923 int klo=0, khi=X.size()-1; 924 925 if(khi < 0) throw c2_exception("Uninitialized interpolating function being evaluated"); 926 927 const float_type *XX=&X[lastKLow]; // make all fast checks short offsets from here 619 928 620 929 if(!xInverted) { // select search depending on whether transformed X is increasing or decreasing 621 if( lastKLow >=0 && (X[lastKLow] <= x) && (X[lastKLow+1] >= x) ) { // already bracketed930 if((XX[0] <= x) && (XX[1] >= x) ) { // already bracketed 622 931 klo=lastKLow; 623 } else if( lastKLow >=0 && (X[lastKLow+1] <= x) && (X[lastKLow+2] >x)) { // in next bracket to the right932 } else if((XX[1] <= x) && (XX[2] >= x)) { // in next bracket to the right 624 933 klo=lastKLow+1; 625 } else if(lastKLow > 0 && (X [lastKLow-1] <= x) && (X[lastKLow] >x)) { // in next bracket to the left934 } else if(lastKLow > 0 && (XX[-1] <= x) && (XX[0] >= x)) { // in next bracket to the left 626 935 klo=lastKLow-1; 627 936 } else { // not bracketed, not close, start over … … 634 943 } 635 944 } else { 636 if( lastKLow >=0 && (X[lastKLow] >= x) && (X[lastKLow+1] <= x) ) { // already bracketed945 if((XX[0] >= x) && (XX[1] <= x) ) { // already bracketed 637 946 klo=lastKLow; 638 } else if( lastKLow >=0 && (X[lastKLow+1] >= x) && (X[lastKLow+2] <x)) { // in next bracket to the right947 } else if((XX[1] >= x) && (XX[2] <= x)) { // in next bracket to the right 639 948 klo=lastKLow+1; 640 } else if(lastKLow > 0 && (X [lastKLow-1] >= x) && (X[lastKLow] <x)) { // in next bracket to the left949 } else if(lastKLow > 0 && (XX[-1] >= x) && (XX[0] <= x)) { // in next bracket to the left 641 950 klo=lastKLow-1; 642 951 } else { // not bracketed, not close, start over … … 659 968 float_type ylo=F[klo], yhi=F[khi], y2lo=y2[klo], y2hi=y2[khi]; 660 969 float_type y=a*ylo+b*yhi+((a*a*a-a)*y2lo+(b*b*b-b)*y2hi)*(h*h)/6.0; 661 662 // template here is impossible! if(fYin && fYin != Identity)663 y=fYout(y); // save time by explicitly testing for identity function here970 971 float_type yp0=0; // the derivative in interpolating table coordinates 972 float_type ypp0=0; // second derivative 664 973 665 974 if(yprime || yprime2) { 666 float_type fpi=1.0/fYinPrime(y); 667 float_type gp=fXinPrime(xraw); 668 float_type yp0=(yhi-ylo)/h+((3*b*b-1)*y2hi-(3*a*a-1)*y2lo)*h/6.0; // the derivative in interpolating table coordinates 669 670 // from Mathematica Dt[InverseFunction[f][y[g[x]]], x] 671 if(yprime) *yprime=gp*yp0*fpi; // the real derivative of the inverse transformed output 672 if(yprime2) { 673 float_type ypp0=b*y2hi+a*y2lo; 674 float_type fpp=fYinDPrime(y); 675 float_type gpp=fXinDPrime(xraw); 676 // also from Mathematica Dt[InverseFunction[f][y[g[x]]], {x,2}] 677 if(yprime2) *yprime2=(gp*gp*ypp0 + yp0*gpp - gp*gp*yp0*yp0*fpp*fpi*fpi)*fpi; 678 } 679 } 680 681 return y; 682 } 683 684 template <typename float_type> void interpolating_function<float_type>::set_lower_extrapolation(float_type bound) 975 yp0=(yhi-ylo)/h+((3*b*b-1)*y2hi-(3*a*a-1)*y2lo)*h/6.0; // the derivative in interpolating table coordinates 976 ypp0=b*y2hi+a*y2lo; // second derivative 977 } 978 979 if(fTransform.isIdentity) { 980 if(yprime) *yprime=yp0; 981 if(yprime2) *yprime2=ypp0; 982 return y; 983 } else return fTransform.evaluate(xraw, y, yp0, ypp0, yprime, yprime2); 984 } 985 986 template <typename float_type> void interpolating_function_p<float_type>::set_lower_extrapolation(float_type bound) 685 987 { 686 988 int kl = 0 ; 687 989 int kh=kl+1; 688 float_type xx=f Xin(bound);990 float_type xx=fTransform.X.fIn(bound); 689 991 float_type h0=X[kh]-X[kl]; 690 992 float_type h1=xx-X[kl]; … … 702 1004 } 703 1005 704 template <typename float_type> void interpolating_function <float_type>::set_upper_extrapolation(float_type bound)1006 template <typename float_type> void interpolating_function_p<float_type>::set_upper_extrapolation(float_type bound) 705 1007 { 706 1008 int kl = X.size()-2 ; 707 1009 int kh=kl+1; 708 float_type xx=f Xin(bound);1010 float_type xx=fTransform.X.fIn(bound); 709 1011 float_type h0=X[kh]-X[kl]; 710 1012 float_type h1=xx-X[kl]; … … 721 1023 } 722 1024 723 // move derivatives into our internal coordinates (use splint to go the other way!)724 template <typename float_type> void interpolating_function<float_type>::localize_derivatives(725 float_type xraw, float_type y, float_type yp, float_type ypp, float_type *y0, float_type *yprime, float_type *yprime2) const726 {727 float_type fp=fYinPrime(y);728 float_type gp=fXinPrime(xraw);729 float_type fpp=fYinDPrime(y);730 float_type gpp=fXinDPrime(xraw);731 732 if(y0) *y0=fYin(y);733 if(yprime) *yprime=yp*fp/gp; // Mathematica Dt[f[y[InverseFunction[g][x]]], x]734 if(yprime2) *yprime2=( yp*yp*fpp - fp*yp*gpp/gp + fp*ypp )/(gp*gp) ; // Mathematica Dt[f[y[InverseFunction[g][x]]], {x,2}]735 }736 737 1025 // return a new interpolating_function which is the unary function of an existing interpolating_function 738 1026 // can also be used to generate a resampling of another c2_function on a different grid … … 740 1028 // and doing b=a.unary_operator(c) where c is a c2_function (probably another interpolating_function) 741 1029 742 template <typename float_type> interpolating_function <float_type>&743 interpolating_function <float_type>::unary_operator(const c2_function<float_type> &source) const1030 template <typename float_type> interpolating_function_p<float_type>& 1031 interpolating_function_p<float_type>::unary_operator(const c2_function<float_type> &source) const 744 1032 { 745 1033 size_t np=X.size(); 746 1034 std::vector<float_type>yv(np); 747 c2_ composed_function<float_type> comp(source, *this);1035 c2_ptr<float_type> comp(source(*this)); 748 1036 float_type yp0, yp1, ypp; 749 1037 750 for(size_t i=0; i<np; i++) { 751 yv[i]=source(fYout(F[i])); // copy pointwise the function of our data values 752 } 753 754 comp(Xraw.front(), &yp0, &ypp); // get derivative at front 755 comp(Xraw.back(), &yp1, &ypp); // get derivative at back 756 757 return *new interpolating_function(Xraw, yv, false, yp0, false, yp1, 758 fXin, fXinPrime, fXinDPrime, 759 fYin, fYinPrime, fYinDPrime, fYout); 1038 for(size_t i=1; i<np-1; i++) { 1039 yv[i]=source(fTransform.Y.fOut(F[i])); // copy pointwise the function of our data values 1040 } 1041 1042 yv.front()=comp(Xraw.front(), &yp0, &ypp); // get derivative at front 1043 yv.back()= comp(Xraw.back(), &yp1, &ypp); // get derivative at back 1044 1045 interpolating_function_p ©=clone(); 1046 copy.load(this->Xraw, yv, false, yp0, false, yp1); 1047 1048 return copy; 760 1049 } 761 1050 762 1051 template <typename float_type> void 763 interpolating_function <float_type>::get_data(std::vector<float_type> &xvals, std::vector<float_type> &yvals) const throw()1052 interpolating_function_p<float_type>::get_data(std::vector<float_type> &xvals, std::vector<float_type> &yvals) const throw() 764 1053 { 765 1054 … … 767 1056 yvals.resize(F.size()); 768 1057 769 for(size_t i=0; i<F.size(); i++) yvals[i]=f Yout(F[i]);770 } 771 772 template <typename float_type> interpolating_function <float_type> &773 interpolating_function <float_type>::binary_operator(const c2_function<float_type> &rhs,774 c 2_binary_function<float_type> *combining_stub) const1058 for(size_t i=0; i<F.size(); i++) yvals[i]=fTransform.Y.fOut(F[i]); 1059 } 1060 1061 template <typename float_type> interpolating_function_p<float_type> & 1062 interpolating_function_p<float_type>::binary_operator(const c2_function<float_type> &rhs, 1063 const c2_binary_function<float_type> *combining_stub) const 775 1064 { 776 1065 size_t np=X.size(); 777 1066 std::vector<float_type> yv(np); 778 c2_constant<float_type> fval; 779 c2_constant<float_type> yval; 1067 c2_constant_p<float_type> fval(0); 780 1068 float_type yp0, yp1, ypp; 781 1069 782 for(size_t i=0; i<np; i++) { 783 fval.reset(fYout(F[i])); // update the constant function pointwise 784 yval.reset(rhs(Xraw[i])); 785 yv[i]=(*combining_stub).combine(fval, yval, Xraw[i], (float_type *)0, (float_type *)0); // compute rhs & combine without derivatives 786 } 787 788 (*combining_stub).combine(*this, rhs, Xraw.front(), &yp0, &ypp); // get derivative at front 789 (*combining_stub).combine(*this, rhs, Xraw.back(), &yp1, &ypp); // get derivative at back 790 791 delete combining_stub; 792 793 return *new interpolating_function(Xraw, yv, false, yp0, false, yp1, 794 fXin, fXinPrime, fXinDPrime, 795 fYin, fYinPrime, fYinDPrime, fYout); 796 } 797 798 template <typename float_type> float_type c2_f_logprime(float_type x) { return 1.0/x; } // the derivative of log(x) 799 template <typename float_type> float_type c2_f_logprime2(float_type x) { return -1.0/(x*x); } // the second derivative of log(x) 800 801 template <typename float_type> log_lin_interpolating_function<float_type>::log_lin_interpolating_function( 802 const std::vector<float_type> &x, const std::vector<float_type> &f, 803 bool lowerSlopeNatural, float_type lowerSlope, 804 bool upperSlopeNatural, float_type upperSlope) 805 : interpolating_function<float_type>() 806 { 807 init(x, f, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, 808 (float_type (*)(float_type)) (std::log), c2_f_logprime, c2_f_logprime2, 0, 0, 0, 0); 809 } 810 811 template <typename float_type> lin_log_interpolating_function<float_type>::lin_log_interpolating_function( 812 const std::vector<float_type> &x, const std::vector<float_type> &f, 813 bool lowerSlopeNatural, float_type lowerSlope, 814 bool upperSlopeNatural, float_type upperSlope) 815 : interpolating_function<float_type>() 816 { 817 init(x, f, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, 818 0, 0, 0, 819 (float_type (*)(float_type)) (std::log), c2_f_logprime, c2_f_logprime2, 820 (float_type (*)(float_type)) (std::exp) ); 821 } 822 823 template <typename float_type> log_log_interpolating_function<float_type>::log_log_interpolating_function( 824 const std::vector<float_type> &x, const std::vector<float_type> &f, 825 bool lowerSlopeNatural, float_type lowerSlope, 826 bool upperSlopeNatural, float_type upperSlope) 827 : interpolating_function<float_type>() 828 { 829 init(x, f, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, 830 (float_type (*)(float_type)) (std::log), c2_f_logprime, c2_f_logprime2, 831 (float_type (*)(float_type)) (std::log), c2_f_logprime, c2_f_logprime2, 832 (float_type (*)(float_type)) (std::exp) ); 833 } 834 835 template <typename float_type> float_type c2_f_recip(float_type x) { return 1.0/x; } 836 template <typename float_type> float_type c2_f_recipprime(float_type x) { return -1.0/(x*x); } // the derivative of 1/x 837 template <typename float_type> float_type c2_f_recipprime2(float_type x) { return 2.0/(x*x*x); } // the second derivative of 1/x 838 839 template <typename float_type> arrhenius_interpolating_function<float_type>::arrhenius_interpolating_function( 840 const std::vector<float_type> &x, const std::vector<float_type> &f, 841 bool lowerSlopeNatural, float_type lowerSlope, 842 bool upperSlopeNatural, float_type upperSlope) 843 : interpolating_function<float_type>() 844 { 845 init(x, f, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, 846 c2_f_recip, c2_f_recipprime, c2_f_recipprime2, 847 (float_type (*)(float_type)) (std::log), c2_f_logprime, c2_f_logprime2, 848 (float_type (*)(float_type)) (std::exp) ); 849 } 850 851 template <typename float_type> c2_inverse_function<float_type>::c2_inverse_function(const c2_function<float_type> &source) 852 : c2_plugin_function<float_type>(source) 1070 c2_const_ptr<float_type> stub(*combining_stub); // manage ownership 1071 1072 for(size_t i=1; i<np-1; i++) { 1073 fval.reset(fTransform.Y.fOut(F[i])); // update the constant function pointwise 1074 yv[i]=combining_stub->combine(fval, rhs, Xraw[i], (float_type *)0, (float_type *)0); // compute rhs & combine without derivatives 1075 } 1076 1077 yv.front()=combining_stub->combine(*this, rhs, Xraw.front(), &yp0, &ypp); // get derivative at front 1078 yv.back()= combining_stub->combine(*this, rhs, Xraw.back(), &yp1, &ypp); // get derivative at back 1079 1080 interpolating_function_p ©=clone(); 1081 copy.load(this->Xraw, yv, false, yp0, false, yp1); 1082 1083 return copy; 1084 } 1085 1086 template <typename float_type> c2_inverse_function_p<float_type>::c2_inverse_function_p(const c2_function<float_type> &source) 1087 : c2_function<float_type>(), func(source) 853 1088 { 854 1089 float_type l=source.xmin(); … … 864 1099 } 865 1100 866 template <typename float_type> float_type c2_inverse_function <float_type>::value_with_derivatives(1101 template <typename float_type> float_type c2_inverse_function_p<float_type>::value_with_derivatives( 867 1102 float_type x, float_type *yprime, float_type *yprime2 868 1103 ) const throw(c2_exception) … … 927 1162 for(int i=0; i<=np; i++) cum[i]*=m; 928 1163 } 929 if(inverse_function) interpolating_function <float_type>(cum, be); // use cum as x axis in inverse function930 else interpolating_function <float_type>(be, cum); // else use lower bin edge as x axis1164 if(inverse_function) interpolating_function_p<float_type>(cum, be); // use cum as x axis in inverse function 1165 else interpolating_function_p<float_type>(be, cum); // else use lower bin edge as x axis 931 1166 std::fill(this->y2.begin(), this->y2.end(), 0.0); // clear second derivatives, to we are piecewise linear 932 1167 } 933 1168 934 template <typename float_type> c2_piecewise_function <float_type>::c2_piecewise_function()1169 template <typename float_type> c2_piecewise_function_p<float_type>::c2_piecewise_function_p() 935 1170 : c2_function<float_type>(), lastKLow(-1) 936 1171 { … … 938 1173 } 939 1174 940 template <typename float_type> c2_piecewise_function<float_type>::~c2_piecewise_function() 941 { 942 size_t np=functions.size(); 943 for(size_t i=0; i<np; i++) if(owns[i]) delete functions[i]; 944 } 945 946 template <typename float_type> float_type c2_piecewise_function<float_type>::value_with_derivatives( 1175 template <typename float_type> c2_piecewise_function_p<float_type>::~c2_piecewise_function_p() 1176 { 1177 } 1178 1179 template <typename float_type> float_type c2_piecewise_function_p<float_type>::value_with_derivatives( 947 1180 float_type x, float_type *yprime, float_type *yprime2 948 1181 ) const throw(c2_exception) … … 974 1207 } 975 1208 976 template <typename float_type> void c2_piecewise_function<float_type>::append_function( 977 c2_function<float_type> &func, bool pass_ownership) throw(c2_exception) 978 { 1209 template <typename float_type> void c2_piecewise_function_p<float_type>::append_function( 1210 const c2_function<float_type> &func) throw(c2_exception) 1211 { 1212 c2_const_ptr<float_type> keepfunc(func); // manage function before we can throw any exceptions 979 1213 if(functions.size()) { // check whether there are any gaps to fill, etc. 980 c 2_function<float_type> &tail=*(functions.back());1214 const c2_function<float_type> &tail=functions.back(); 981 1215 float_type x0=tail.xmax(); 982 1216 float_type x1=func.xmin(); … … 985 1219 float_type y0=tail(x0); 986 1220 float_type y1=func(x1); 987 c2_function<float_type> *connector=new c2_linear<float_type>(x0, y0, (y1-y0)/(x1-x0)); 988 connector->set_domain(x0,x1); 989 functions.push_back(connector); 990 owns.push_back(true); 1221 c2_function<float_type> &connector=*new c2_linear_p<float_type>(x0, y0, (y1-y0)/(x1-x0)); 1222 connector.set_domain(x0,x1); 1223 functions.push_back(c2_const_ptr<float_type>(connector)); 991 1224 this->sampling_grid->push_back(x1); 992 1225 } else if(x0>x1) throw c2_exception("function domains not increasing in c2_piecewise_function"); 993 1226 } 994 functions.push_back(&func); 995 owns.push_back(pass_ownership); 1227 functions.push_back(keepfunc); 996 1228 // extend our domain to include all known functions 997 1229 this->set_domain(functions.front()->xmin(), functions.back()->xmax()); 998 1230 // extend our sampling grid with the new function's grid, with the first point dropped to avoid duplicates 999 std::vector<float_type> &newgrid=func.get_sampling_grid(func.xmin(), func.xmax()); 1231 std::vector<float_type> newgrid; 1232 func.get_sampling_grid(func.xmin(), func.xmax(), newgrid); 1000 1233 this->sampling_grid->insert(this->sampling_grid->end(), newgrid.begin()+1, newgrid.end()); 1001 delete &newgrid; 1002 } 1003 1004 template <typename float_type> c2_connector_function<float_type>::c2_connector_function( 1005 const c2_function<float_type> &f1, const c2_function<float_type> &f2, float_type x0, float_type x2, 1234 } 1235 1236 template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p( 1237 float_type x0, const c2_function<float_type> &f0, float_type x2, const c2_function<float_type> &f2, 1006 1238 bool auto_center, float_type y1) 1007 1008 : c2_function<float_type>() 1009 { 1010 float_type y0, yp0, ypp0, y2, yp2, ypp2; 1011 fdx=(x2-x0)/2.0; 1012 fhinv=1.0/fdx; 1013 fx1=(x0+x2)/2.0; 1014 1015 y0=f1.value_with_derivatives(x0, &yp0, &ypp0); // get left wall values from conventional computation 1016 y2=f2.value_with_derivatives(x2, &yp2, &ypp2); // get right wall values from conventional computation 1239 : c2_function<float_type>() 1240 { 1241 c2_const_ptr<float_type> left(f0), right(f2); // make sure if these are unowned, they get deleted 1242 c2_fblock<float_type> fb0, fb2; 1243 fb0.x=x0; 1244 f0.fill_fblock(fb0); 1245 fb2.x=x2; 1246 f2.fill_fblock(fb2); 1247 init(fb0, fb2, auto_center, y1); 1248 } 1249 1250 template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p( 1251 float_type x0, float_type y0, float_type yp0, float_type ypp0, 1252 float_type x2, float_type y2, float_type yp2, float_type ypp2, 1253 bool auto_center, float_type y1) 1254 : c2_function<float_type>() 1255 { 1256 c2_fblock<float_type> fb0, fb2; 1257 fb0.x=x0; fb0.y=y0; fb0.yp=yp0; fb0.ypp=ypp0; 1258 fb2.x=x2; fb2.y=y2; fb2.yp=yp2; fb2.ypp=ypp2; 1259 init(fb0, fb2, auto_center, y1); 1260 } 1261 1262 template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p( 1263 const c2_fblock<float_type> &fb0, 1264 const c2_fblock<float_type> &fb2, 1265 bool auto_center, float_type y1) 1266 : c2_function<float_type>() 1267 { 1268 init(fb0, fb2, auto_center, y1); 1269 } 1270 1271 template <typename float_type> void c2_connector_function_p<float_type>::init( 1272 const c2_fblock<float_type> &fb0, 1273 const c2_fblock<float_type> &fb2, 1274 bool auto_center, float_type y1) 1275 { 1276 float_type dx=(fb2.x-fb0.x)/2.0; 1277 fhinv=1.0/dx; 1017 1278 1018 1279 // scale derivs to put function on [-1,1] since mma solution is done this way 1019 yp0*=fdx;1020 yp2*=fdx;1021 ypp0*=fdx*fdx;1022 ypp2*=fdx*fdx;1023 1024 float_type ff0=(8*( y0 + y2) + 5*(yp0 - yp2) + ypp0 + ypp2)/16.0;1280 float_type yp0=fb0.yp*dx; 1281 float_type yp2=fb2.yp*dx; 1282 float_type ypp0=fb0.ypp*dx*dx; 1283 float_type ypp2=fb2.ypp*dx*dx; 1284 1285 float_type ff0=(8*(fb0.y + fb2.y) + 5*(yp0 - yp2) + ypp0 + ypp2)*0.0625; 1025 1286 if(auto_center) y1=ff0; // forces ff to be 0 if we are auto-centering 1026 1287 1027 // y[x_] = y1 + x (a + b x) + (x-1) x (x+1) (c + d x + e x^2 + f x^3) 1288 // y[x_] = y1 + x (a + b x) + x [(x-1) (x+1)] (c + d x) + x (x-1)^2 (x+1)^2 (e + f x) 1289 // y' = a + 2 b x + d x [(x+1)(x-1)] + (c + d x)(3x^2-1) + f x [(x+1)(x-1)]^2 + (e + f x)[(x+1)(x-1)](5x^2-1) 1290 // y'' = 2 b + 6x(c + d x) + 2d(3x^2-1) + 4x(e + f x)(5x^2-3) + 2f(x^2-1)(5x^2-1) 1028 1291 fy1=y1; 1029 fa= -(y0 - y2)/2.;1030 fb=( y0 - 2*y1 + y2)/2.;1031 fc=( 7*(y0 - y2 + yp0 + yp2) + ypp0 - ypp2)/16.;1032 fd=( 32*y1 - 16*(y2 + y0) + 9*(yp2 - yp0) - ypp0 - ypp2)/16.;1033 fe=( 3*(y2 - y0 - yp0 - yp2) - ypp0 + ypp2)/16.;1292 fa=(fb2.y - fb0.y)*0.5; 1293 fb=(fb0.y + fb2.y)*0.5 - y1; 1294 fc=(yp2+yp0-2.*fa)*0.25; 1295 fd=(yp2-yp0-4.*fb)*0.25; 1296 fe=(ypp2-ypp0-12.*fc)*0.0625; 1034 1297 ff=(ff0 - y1); 1035 // y'[x] = a + 2 b x + (3x^2 - 1) (c + d x + e x^2 + f x^3) + (x-1) x (x+1) (d + 2 e x + 3 f x^2 ) 1036 // y''[x] = 2b + (x-1) x (x+1) (2 e + 6 f x) + 2 (3 x^2 -1) (d + 2 e x + 3 f x^2 ) + 6 x (c + d x + e x^2 + f x^3) 1037 this->set_domain(x0,x2); // this is where the function is valid 1038 } 1039 1040 template <typename float_type> c2_connector_function<float_type>::~c2_connector_function() 1041 { 1042 } 1043 1044 template <typename float_type> float_type c2_connector_function<float_type>::value_with_derivatives( 1298 this->set_domain(fb0.x, fb2.x); // this is where the function is valid 1299 } 1300 1301 template <typename float_type> c2_connector_function_p<float_type>::~c2_connector_function_p() 1302 { 1303 } 1304 1305 template <typename float_type> float_type c2_connector_function_p<float_type>::value_with_derivatives( 1045 1306 float_type x, float_type *yprime, float_type *yprime2 1046 1307 ) const throw(c2_exception) 1047 1308 { 1048 1049 float_type dx=(x-fx1)*fhinv; 1050 float_type q1=fc + dx*(fd + dx*(fe + dx*ff)); 1051 float_type xp1=(dx-1)*(dx+1)*dx; 1052 1053 float_type y= fy1 + dx*(fa+fb*dx) + xp1*q1; 1309 float_type x0=this->xmin(), x2=this->xmax(); 1310 float_type dx=(x-(x0+x2)*0.5)*fhinv; 1311 float_type q1=(x-x0)*(x-x2)*fhinv*fhinv; // exactly vanish all bits at both ends 1312 float_type q2=dx*q1; 1313 1314 float_type r1=fa+fb*dx; 1315 float_type r2=fc+fd*dx; 1316 float_type r3=fe+ff*dx; 1317 1318 float_type y=fy1+dx*r1+q2*r2+q1*q2*r3; 1319 1054 1320 if(yprime || yprime2) { 1055 float_type q2 =fd + dx*(2*fe + dx*3*ff); 1056 float_type q3=2*fe+6*ff*dx; 1057 float_type xp2=(3*dx*dx-1); 1058 if(yprime) *yprime=(fa + 2*fb*dx + xp2*q1 + xp1*q2)*fhinv; 1059 if(yprime2) *yprime2=(2*fb+xp1*q3+2*xp2*q2+6*dx*q1)*fhinv*fhinv; 1321 float_type q3=3*q1+2; 1322 float_type q4=5*q1+4; 1323 if(yprime) *yprime=(fa+2*fb*dx+fd*q2+r2*q3+ff*q1*q2+q1*q4*r3)*fhinv; 1324 if(yprime2) *yprime2=2*(fb+fd*q3+3*dx*r2+ff*q1*q4+r3*(2*dx*(5*q1+2)))*fhinv*fhinv; 1060 1325 } 1061 1326 return y; 1062 1327 } 1328 1329 // the recursive part of the sampler is agressively designed to minimize copying of data... lots of pointers 1330 template <typename float_type> void c2_function<float_type>::sample_step(c2_sample_recur &rb) const throw(c2_exception) 1331 { 1332 std::vector< recur_item > &rb_stack=*rb.rb_stack; // heap-based stack of data for recursion 1333 rb_stack.clear(); 1334 1335 recur_item top; 1336 top.depth=0; top.done=false; top.f0index=0; top.f2index=0; 1337 1338 // push storage for our initial elements 1339 rb_stack.push_back(top); 1340 rb_stack.back().f1=*rb.f0; 1341 rb_stack.back().done=true; 1342 1343 rb_stack.push_back(top); 1344 rb_stack.back().f1=*rb.f1; 1345 rb_stack.back().done=true; 1346 1347 if(!rb.inited) { 1348 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon(); 1349 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min(); 1350 rb.inited=true; 1351 } 1352 1353 // now, push our first real element 1354 top.f0index=0; // left element is stack[0] 1355 top.f2index=1; // right element is stack[1] 1356 rb_stack.push_back(top); 1357 1358 while(rb_stack.size() > 2) { 1359 recur_item &back=rb_stack.back(); 1360 if(back.done) { 1361 rb_stack.pop_back(); 1362 continue; 1363 } 1364 back.done=true; 1365 1366 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, &f2=rb_stack[back.f2index].f1; 1367 c2_fblock<float_type> &f1=back.f1; // will hold new middle values 1368 size_t f1index=rb_stack.size()-1; // our current offset 1369 1370 // std::cout << "processing: " << rb_stack.size() << " " << 1371 // (&back-&rb_stack.front()) << " " << back.depth << " " << f0.x << " " << f2.x << std::endl; 1372 1373 f1.x=0.5*(f0.x + f2.x); // center of interval 1374 float_type dx2=0.5*(f2.x - f0.x); 1375 1376 // check for underflow on step size, which prevents us from achieving specified accuracy. 1377 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2) < rb.abs_tol_min) { 1378 std::ostringstream outstr; 1379 outstr << "Step size underflow in adaptive_sampling at depth=" << back.depth << ", x= " << f1.x; 1380 throw c2_exception(outstr.str().c_str()); 1381 } 1382 1383 fill_fblock(f1); 1384 1385 if(c2_isnan(f1.y) || f1.ypbad || f1.yppbad) { 1386 // can't go any further if a nan has appeared 1387 bad_x_point=f1.x; 1388 throw c2_exception("NaN encountered while sampling function"); 1389 } 1390 1391 float_type eps; 1392 if(rb.derivs==2) { 1393 // this is code from connector_function to compute the value at the midpoint 1394 // it is re-included here to avoid constructing a complete c2connector 1395 // just to find out if we are close enough 1396 float_type ff0=(8*(f0.y + f2.y) + 5*(f0.yp - f2.yp)*dx2 + (f0.ypp+f2.ypp)*dx2*dx2)*0.0625; 1397 // we are converging as at least x**5 and bisecting, so real error on final step is smaller 1398 eps=std::abs(ff0-f1.y)/32.0; 1399 } else { 1400 // there are two tolerances to meet... the shift in the estimate of the actual point, 1401 // and the difference between the current points and the extremum 1402 // build all the coefficients needed to construct the local parabola 1403 float_type ypcenter, ypp; 1404 if (rb.derivs==1) { 1405 // linear extrapolation error using exact derivs 1406 eps = (std::abs(f0.y+f0.yp*dx2-f1.y)+std::abs(f2.y-f2.yp*dx2-f1.y))*0.125; 1407 ypcenter=2*f1.yp*dx2; // first deriv scaled so this interval is on [-1,1] 1408 ypp=2*(f2.yp-f0.yp)*dx2*dx2; // second deriv estimate scaled so this interval is on [-1,1] 1409 } else { 1410 // linear interpolation error without derivs if we are at top level 1411 // or 3-point parabolic interpolation estimates from previous level, if available 1412 ypcenter=(f2.y-f0.y)*0.5; // derivative estimate at center 1413 ypp=(f2.y+f0.y-2*f1.y); // second deriv estimate 1414 if(back.depth==0) eps=std::abs((f0.y+f2.y)*0.5 - f1.y)*2; // penalize first step 1415 else eps=std::abs(f1.y-back.previous_estimate)*0.25; 1416 } 1417 float_type ypleft=ypcenter-ypp; // derivative at left edge 1418 float_type ypright=ypcenter+ypp; // derivative at right edge 1419 float_type extremum_eps=0; 1420 if((ypleft*ypright) <=0) // y' changes sign if we have an extremum 1421 { 1422 // compute position and value of the extremum this way 1423 float_type xext=-ypcenter/ypp; 1424 float_type yext=f1.y + xext*ypcenter + 0.5*xext*xext*ypp; 1425 // and then find the the smallest offset of it from a point, looking in the left or right side 1426 if(xext <=0) extremum_eps=std::min(std::abs(f0.y-yext), std::abs(f1.y-yext)); 1427 else extremum_eps=std::min(std::abs(f2.y-yext), std::abs(f1.y-yext)); 1428 } 1429 eps=std::max(eps, extremum_eps); // if previous shot was really bad, keep trying 1430 } 1431 1432 if(eps < rb.abs_tol || eps < std::abs(f1.y)*rb.rel_tol) { 1433 if(rb.out) { 1434 // we've met the tolerance, and are building a function, append two connectors 1435 rb.out->append_function( 1436 *new c2_connector_function_p<float_type>(f0, f1, true, 0.0) 1437 ); 1438 rb.out->append_function( 1439 *new c2_connector_function_p<float_type>(f1, f2, true, 0.0) 1440 ); 1441 } 1442 if(rb.xvals && rb.yvals) { 1443 rb.xvals->push_back(f0.x); 1444 rb.xvals->push_back(f1.x); 1445 rb.yvals->push_back(f0.y); 1446 rb.yvals->push_back(f1.y); 1447 // the value at f2 will get pushed in the next segment... it is not forgotten 1448 } 1449 } else { 1450 top.depth=back.depth+1; // increment depth counter 1451 1452 // save the last things we need from back before a push happens, in case 1453 // the push causes a reallocation and moves the whole stack. 1454 size_t f0index=back.f0index, f2index=back.f2index; 1455 float_type left=0, right=0; 1456 if(rb.derivs==0) { 1457 // compute three-point parabolic interpolation estimate of right-hand and left-hand midpoint 1458 left=(6*f1.y + 3*f0.y - f2.y) * 0.125; 1459 right=(6*f1.y + 3*f2.y - f0.y) * 0.125; 1460 } 1461 1462 top.f0index=f1index; top.f2index=f2index; // insert pointers to right side data into our recursion block 1463 top.previous_estimate=right; 1464 rb_stack.push_back(top); 1465 1466 top.f0index=f0index; top.f2index=f1index; // insert pointers to left side data into our recursion block 1467 top.previous_estimate=left; 1468 rb_stack.push_back(top); 1469 } 1470 } 1471 } 1472 1473 template <typename float_type> c2_piecewise_function_p<float_type> * 1474 c2_function<float_type>::adaptively_sample( 1475 float_type xmin, float_type xmax, 1476 float_type abs_tol, float_type rel_tol, 1477 int derivs, std::vector<float_type> *xvals, std::vector<float_type> *yvals) const throw(c2_exception) 1478 { 1479 c2_fblock<float_type> f0, f2; 1480 c2_sample_recur rb; 1481 std::vector< recur_item > rb_stack; 1482 rb_stack.reserve(20); // enough for most operations 1483 rb.rb_stack=&rb_stack; 1484 rb.out=0; 1485 if(derivs==2) rb.out=new c2_piecewise_function_p<float_type>(); 1486 c2_ptr<float_type> pieces(*rb.out); // manage this function, if any, so it deletes on an exception 1487 rb.rel_tol=rel_tol; 1488 rb.abs_tol=abs_tol; 1489 rb.xvals=xvals; 1490 rb.yvals=yvals; 1491 rb.derivs=derivs; 1492 rb.inited=false; 1493 1494 if(xvals && yvals) { 1495 xvals->clear(); 1496 yvals->clear(); 1497 } 1498 1499 // create xgrid as a automatic-variable copy of the sampling grid so the exception handler correctly 1500 // disposes of it. 1501 std::vector<float_type> xgrid; 1502 get_sampling_grid(xmin, xmax, xgrid); 1503 int np=xgrid.size(); 1504 1505 f2.x=xgrid[0]; 1506 fill_fblock(f2); 1507 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) { 1508 // can't go any further if a nan has appeared 1509 bad_x_point=f2.x; 1510 throw c2_exception("NaN encountered while sampling function"); 1511 } 1512 1513 for(int i=0; i<np-1; i++) { 1514 f0=f2; // copy upper bound to lower before computing new upper bound 1515 1516 f2.x=xgrid[i+1]; 1517 fill_fblock(f2); 1518 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) { 1519 // can't go any further if a nan has appeared 1520 bad_x_point=f2.x; 1521 throw c2_exception("NaN encountered while sampling function"); 1522 } 1523 1524 rb.f0=&f0; rb.f1=&f2; 1525 sample_step(rb); 1526 } 1527 if(xvals && yvals) { // push final point in vector 1528 xvals->push_back(f2.x); 1529 yvals->push_back(f2.y); 1530 } 1531 1532 if(rb.out) rb.out->set_sampling_grid(xgrid); // reflect old sampling grid, which still should be right 1533 pieces.release_for_return(); // unmanage the piecewise_function so we can return it 1534 return rb.out; 1535 } 1536 1537 template <typename float_type, typename Final> 1538 interpolating_function_p<float_type> & inverse_integrated_density_function( 1539 const std::vector<float_type> &bincenters, const c2_function<float_type> &binheights) 1540 throw(c2_exception) 1541 { 1542 return (new Final())->load_random_generator_function(bincenters, binheights); 1543 } 1544 1545 template <typename float_type, typename Final> 1546 interpolating_function_p<float_type> & inverse_integrated_density_bins( 1547 const std::vector<float_type> &bins, const std::vector<float_type> &binheights) 1548 throw(c2_exception) 1549 { 1550 return (new Final())->load_random_generator_bins(bins, binheights); 1551 }
Note: See TracChangeset
for help on using the changeset viewer.