|
| 1 | +/** |
| 2 | + * @file |
| 3 | + * |
| 4 | + * A vibrational (IR / Raman) spectrum as the Vibrational Frequencies |
| 5 | + * panel shows it (#214). |
| 6 | + * |
| 7 | + * No wx and no drawing: which sticks exist, what a scaling factor and a |
| 8 | + * line width do to them, which wavenumber range is on screen and where |
| 9 | + * the ticks go are decided here, so they can be tested from the command |
| 10 | + * line. SpectrumCanvas.H only paints what this holds. |
| 11 | + */ |
| 12 | + |
| 13 | +#ifndef VIBSPECTRUM_HH |
| 14 | +#define VIBSPECTRUM_HH |
| 15 | + |
| 16 | +#include <string> |
| 17 | +#include <vector> |
| 18 | +using std::string; |
| 19 | +using std::vector; |
| 20 | + |
| 21 | +/** The two spectra a vibrational calculation can give. */ |
| 22 | +enum VibKind { VIB_IR = 0, VIB_RAMAN = 1 }; |
| 23 | + |
| 24 | +/** The wavenumber axis: a window on the data and its direction. */ |
| 25 | +class SpectrumAxis |
| 26 | +{ |
| 27 | + public: |
| 28 | + SpectrumAxis(); |
| 29 | + |
| 30 | + /** Set the whole range the data spans; resets the view to it. */ |
| 31 | + void setFull(double lo, double hi); |
| 32 | + double fullLo() const { return p_fullLo; } |
| 33 | + double fullHi() const { return p_fullHi; } |
| 34 | + |
| 35 | + double lo() const { return p_lo; } |
| 36 | + double hi() const { return p_hi; } |
| 37 | + double span() const { return p_hi - p_lo; } |
| 38 | + bool zoomed() const; |
| 39 | + |
| 40 | + /** High wavenumbers on the left, the convention for IR and Raman. */ |
| 41 | + void setReversed(bool reversed) { p_reversed = reversed; } |
| 42 | + bool reversed() const { return p_reversed; } |
| 43 | + |
| 44 | + /** 0 at the left edge of the plot, 1 at the right. */ |
| 45 | + double toFraction(double wavenumber) const; |
| 46 | + double fromFraction(double fraction) const; |
| 47 | + |
| 48 | + /** Show [a, b] (either order), clipped to the full range. */ |
| 49 | + void zoomTo(double a, double b); |
| 50 | + /** Scale the window by factor (<1 zooms in) about a wavenumber. */ |
| 51 | + void zoomAbout(double centre, double factor); |
| 52 | + /** Move the window by a wavenumber difference, staying in range. */ |
| 53 | + void pan(double delta); |
| 54 | + void reset(); |
| 55 | + |
| 56 | + /** Narrowest window allowed, cm-1. */ |
| 57 | + static double minSpan() { return 20.0; } |
| 58 | + |
| 59 | + private: |
| 60 | + void clamp(); |
| 61 | + |
| 62 | + double p_fullLo, p_fullHi, p_lo, p_hi; |
| 63 | + bool p_reversed; |
| 64 | +}; |
| 65 | + |
| 66 | +/** Tick positions for an axis: round numbers, 1, 2 or 5 times 10^n. */ |
| 67 | +struct SpectrumTicks |
| 68 | +{ |
| 69 | + vector<double> values; |
| 70 | + double step; |
| 71 | + int decimals; // digits after the point a label needs |
| 72 | +}; |
| 73 | + |
| 74 | +/** Ticks inside [lo, hi], at most about `target` of them. */ |
| 75 | +SpectrumTicks niceTicks(double lo, double hi, int target); |
| 76 | + |
| 77 | +/** |
| 78 | + * A y axis from 0 up: `top` is the first tick at or above the maximum, |
| 79 | + * so the tallest feature is never clipped. |
| 80 | + */ |
| 81 | +struct SpectrumYAxis |
| 82 | +{ |
| 83 | + double top; |
| 84 | + SpectrumTicks ticks; |
| 85 | +}; |
| 86 | +SpectrumYAxis niceYAxis(double maximum, int target); |
| 87 | + |
| 88 | +/** One mode, with the quantities drawn for it. */ |
| 89 | +struct VibStick |
| 90 | +{ |
| 91 | + int mode; // zero-based row of the frequency table |
| 92 | + double wavenumber; // scaled, cm-1; negative if imaginary |
| 93 | + double intensity; // as drawn (see VibSpectrum::unitLabel) |
| 94 | + string irrep; |
| 95 | + bool imaginary; |
| 96 | +}; |
| 97 | + |
| 98 | +enum LineShape { LINE_GAUSSIAN, LINE_LORENTZIAN }; |
| 99 | + |
| 100 | +class VibSpectrum |
| 101 | +{ |
| 102 | + public: |
| 103 | + VibSpectrum(); |
| 104 | + |
| 105 | + /** |
| 106 | + * The modes, from VIBFREQ and (optionally) VIBSYM. Clears any |
| 107 | + * intensities. `irreps` may be empty or shorter than the list. |
| 108 | + */ |
| 109 | + void setModes(const vector<double>& wavenumbers, |
| 110 | + const vector<string>& irreps); |
| 111 | + |
| 112 | + /** |
| 113 | + * Intensities for every mode, in the code's own `units` as written |
| 114 | + * in the property (e.g. "KM/Mole", "A^4/AMU"). A units string that |
| 115 | + * is not recognised makes the spectrum relative: drawn scaled so |
| 116 | + * its largest stick is 1. |
| 117 | + */ |
| 118 | + void setIntensities(VibKind kind, const vector<double>& values, |
| 119 | + const string& units); |
| 120 | + |
| 121 | + bool has(VibKind kind) const { return p_has[kind]; } |
| 122 | + int modeCount() const { return (int)p_freq.size(); } |
| 123 | + |
| 124 | + /** Axis title for the pane, e.g. "IR intensity (km/mol)". */ |
| 125 | + string axisLabel(VibKind kind) const; |
| 126 | + /** Just the unit, UTF-8: "km/mol", "Å⁴/amu", "relative". */ |
| 127 | + string unitLabel(VibKind kind) const; |
| 128 | + bool relative(VibKind kind) const { return p_relative[kind]; } |
| 129 | + |
| 130 | + /** Frequency scaling factor, applied to every wavenumber. */ |
| 131 | + void setScale(double scale) { p_scale = scale > 0 ? scale : 1.0; } |
| 132 | + double scale() const { return p_scale; } |
| 133 | + |
| 134 | + /** |
| 135 | + * Full width at half maximum of the broadening, cm-1. 0 means |
| 136 | + * sticks only. |
| 137 | + */ |
| 138 | + void setFwhm(double fwhm) { p_fwhm = fwhm > 0 ? fwhm : 0.0; } |
| 139 | + double fwhm() const { return p_fwhm; } |
| 140 | + void setLineShape(LineShape shape) { p_shape = shape; } |
| 141 | + LineShape lineShape() const { return p_shape; } |
| 142 | + |
| 143 | + /** |
| 144 | + * Wavenumbers below this magnitude are the translations and |
| 145 | + * rotations some codes list among the modes (ORCA prints 0.00, NWChem |
| 146 | + * values of 1e-5). They are not vibrations, so they get no stick. |
| 147 | + */ |
| 148 | + static double zeroTolerance() { return 10.0; } |
| 149 | + |
| 150 | + /** Scaled wavenumber of mode i. */ |
| 151 | + double wavenumber(int i) const { return p_freq[i] * p_scale; } |
| 152 | + bool isVibration(int i) const; |
| 153 | + bool isImaginary(int i) const; |
| 154 | + |
| 155 | + /** |
| 156 | + * The sticks of one spectrum: real vibrations and imaginary modes, |
| 157 | + * not the near-zero ones. A mode whose intensity is zero stays in |
| 158 | + * the list, so it can still be hovered and so the table row it |
| 159 | + * belongs to is known. |
| 160 | + */ |
| 161 | + vector<VibStick> sticks(VibKind kind) const; |
| 162 | + |
| 163 | + /** |
| 164 | + * The broadened spectrum at a wavenumber, in intensity per cm-1: |
| 165 | + * each real mode contributes an area-normalised line, so the |
| 166 | + * integral of a band is its stick intensity whatever the width and |
| 167 | + * overlapping bands add. Imaginary modes contribute nothing. |
| 168 | + */ |
| 169 | + double envelope(VibKind kind, double wavenumber) const; |
| 170 | + |
| 171 | + /** Largest stick height with wavenumber in [lo, hi]; 0 if none. */ |
| 172 | + double maxStick(VibKind kind, double lo, double hi) const; |
| 173 | + /** Largest envelope value in [lo, hi] (0 when fwhm is 0). */ |
| 174 | + double maxEnvelope(VibKind kind, double lo, double hi) const; |
| 175 | + |
| 176 | + /** |
| 177 | + * The wavenumber range worth showing by default: the modes plus a |
| 178 | + * margin, on round numbers. Reaches below zero only when there is |
| 179 | + * an imaginary mode. |
| 180 | + */ |
| 181 | + void defaultRange(double *lo, double *hi) const; |
| 182 | + |
| 183 | + /** Area-normalised line shape, 1/cm-1, centred on zero. */ |
| 184 | + static double lineValue(LineShape shape, double offset, double fwhm); |
| 185 | + |
| 186 | + /** Whether the code's units string is one we know how to label. */ |
| 187 | + static bool knownIntensityUnits(VibKind kind, const string& units); |
| 188 | + |
| 189 | + private: |
| 190 | + double drawn(VibKind kind, int i) const; |
| 191 | + |
| 192 | + vector<double> p_freq; |
| 193 | + vector<string> p_irrep; |
| 194 | + vector<double> p_inten[2]; |
| 195 | + bool p_has[2]; |
| 196 | + bool p_relative[2]; |
| 197 | + string p_units[2]; |
| 198 | + double p_scale, p_fwhm; |
| 199 | + LineShape p_shape; |
| 200 | +}; |
| 201 | + |
| 202 | +#endif |
0 commit comments