PySDM_examples.Shima_et_al_2009.spectrum_plotter
1import matplotlib 2import numpy as np 3from matplotlib import pyplot 4from open_atmos_jupyter_utils import show_plot 5from packaging import version 6from PySDM_examples.Shima_et_al_2009.error_measure import error_measure 7 8from PySDM.physics.constants import si 9 10_matplotlib_version_3_3_3 = version.parse("3.3.0") 11_matplotlib_version_actual = version.parse(matplotlib.__version__) 12 13 14class SpectrumColors: 15 def __init__(self, begining="#2cbdfe", end="#b317b1"): 16 self.b = begining 17 self.e = end 18 19 def __call__(self, value: float): 20 bR, bG, bB = int(self.b[1:3], 16), int(self.b[3:5], 16), int(self.b[5:7], 16) 21 eR, eG, eB = int(self.e[1:3], 16), int(self.e[3:5], 16), int(self.e[5:7], 16) 22 R = bR + int((eR - bR) * value) 23 G = bG + int((eG - bG) * value) 24 B = bB + int((eB - bB) * value) 25 result = f"#{hex(R)[2:4]}{hex(G)[2:4]}{hex(B)[2:4]}" 26 return result 27 28 29class SpectrumPlotter: 30 def __init__(self, settings, title=None, grid=True, legend=True, log_base=10): 31 self.settings = settings 32 self.format = "pdf" 33 self.colors = SpectrumColors() 34 self.smooth = False 35 self.smooth_scope = 2 36 self.legend = legend 37 self.grid = grid 38 self.title = title 39 self.xlabel = "particle radius [µm]" 40 self.ylabel = "dm/dlnr [g/m^3/(unit dr/r)]" 41 self.log_base = log_base 42 self.ax = pyplot 43 self.fig = pyplot 44 self.finished = False 45 46 def finish(self): 47 if self.finished: 48 return 49 self.finished = True 50 if self.grid: 51 self.ax.grid() 52 53 base_arg = { 54 "base" 55 + ( 56 "x" if _matplotlib_version_actual < _matplotlib_version_3_3_3 else "" 57 ): self.log_base 58 } 59 if self.title is not None: 60 try: 61 self.ax.title(self.title) 62 except TypeError: 63 self.ax.set_title(self.title) 64 try: 65 self.ax.xscale("log", **base_arg) 66 self.ax.xlabel(self.xlabel) 67 self.ax.ylabel(self.ylabel) 68 except AttributeError: 69 self.ax.set_xscale("log", **base_arg) 70 self.ax.set_xlabel(self.xlabel) 71 self.ax.set_ylabel(self.ylabel) 72 if self.legend: 73 self.ax.legend() 74 75 def show(self): 76 self.finish() 77 pyplot.tight_layout() 78 show_plot() 79 80 def save(self, file): 81 self.finish() 82 pyplot.savefig(file, format=self.format) 83 84 def plot(self, spectrum, t): 85 error = self.plot_analytic_solution(self.settings, t, spectrum) 86 self.plot_data(self.settings, t, spectrum) 87 return error 88 89 def plot_analytic_solution(self, settings, t, spectrum=None): 90 if t == 0: 91 analytic_solution = settings.spectrum.size_distribution 92 else: 93 94 def analytic_solution(x): 95 return settings.norm_factor * settings.kernel.analytic_solution( 96 x=x, t=t, x_0=settings.X0, N_0=settings.n_part 97 ) 98 99 volume_bins_edges = self.settings.formulae.trivia.volume( 100 settings.radius_bins_edges 101 ) 102 dm = np.diff(volume_bins_edges) 103 dr = np.diff(settings.radius_bins_edges) 104 105 pdf_m_x = volume_bins_edges[:-1] + dm / 2 106 pdf_m_y = analytic_solution(pdf_m_x) 107 108 pdf_r_x = settings.radius_bins_edges[:-1] + dr / 2 109 pdf_r_y = pdf_m_y * dm / dr * pdf_r_x 110 111 x = pdf_r_x * si.metres / si.micrometres 112 y_true = ( 113 pdf_r_y 114 * self.settings.formulae.trivia.volume(radius=pdf_r_x) 115 * settings.rho 116 / settings.dv 117 * si.kilograms 118 / si.grams 119 ) 120 121 self.ax.plot(x, y_true, color="black") 122 123 if spectrum is not None: 124 y = spectrum * si.kilograms / si.grams 125 error = error_measure(y, y_true, x) 126 self.title = f"error measure: {error:.2f}" # TODO #327 relative error 127 return error 128 return None 129 130 def plot_data(self, settings, t, spectrum): 131 if self.smooth: 132 scope = self.smooth_scope 133 if t != 0: 134 new = np.copy(spectrum) 135 for _ in range(2): 136 for i in range(scope, len(spectrum) - scope): 137 new[i] = np.mean(spectrum[i - scope : i + scope + 1]) 138 scope = 1 139 for i in range(scope, len(spectrum) - scope): 140 spectrum[i] = np.mean(new[i - scope : i + scope + 1]) 141 142 x = settings.radius_bins_edges[:-scope] 143 dx = np.diff(x) 144 self.ax.plot( 145 (x[:-1] + dx / 2) * si.metres / si.micrometres, 146 spectrum[:-scope] * si.kilograms / si.grams, 147 label=f"t = {t}s", 148 color=self.colors( 149 t / (self.settings.output_steps[-1] * self.settings.dt) 150 ), 151 ) 152 else: 153 self.ax.step( 154 settings.radius_bins_edges[:-1] * si.metres / si.micrometres, 155 spectrum * si.kilograms / si.grams, 156 where="post", 157 label=f"t = {t}s", 158 color=self.colors( 159 t / (self.settings.output_steps[-1] * self.settings.dt) 160 ), 161 )
class
SpectrumColors:
15class SpectrumColors: 16 def __init__(self, begining="#2cbdfe", end="#b317b1"): 17 self.b = begining 18 self.e = end 19 20 def __call__(self, value: float): 21 bR, bG, bB = int(self.b[1:3], 16), int(self.b[3:5], 16), int(self.b[5:7], 16) 22 eR, eG, eB = int(self.e[1:3], 16), int(self.e[3:5], 16), int(self.e[5:7], 16) 23 R = bR + int((eR - bR) * value) 24 G = bG + int((eG - bG) * value) 25 B = bB + int((eB - bB) * value) 26 result = f"#{hex(R)[2:4]}{hex(G)[2:4]}{hex(B)[2:4]}" 27 return result
class
SpectrumPlotter:
30class SpectrumPlotter: 31 def __init__(self, settings, title=None, grid=True, legend=True, log_base=10): 32 self.settings = settings 33 self.format = "pdf" 34 self.colors = SpectrumColors() 35 self.smooth = False 36 self.smooth_scope = 2 37 self.legend = legend 38 self.grid = grid 39 self.title = title 40 self.xlabel = "particle radius [µm]" 41 self.ylabel = "dm/dlnr [g/m^3/(unit dr/r)]" 42 self.log_base = log_base 43 self.ax = pyplot 44 self.fig = pyplot 45 self.finished = False 46 47 def finish(self): 48 if self.finished: 49 return 50 self.finished = True 51 if self.grid: 52 self.ax.grid() 53 54 base_arg = { 55 "base" 56 + ( 57 "x" if _matplotlib_version_actual < _matplotlib_version_3_3_3 else "" 58 ): self.log_base 59 } 60 if self.title is not None: 61 try: 62 self.ax.title(self.title) 63 except TypeError: 64 self.ax.set_title(self.title) 65 try: 66 self.ax.xscale("log", **base_arg) 67 self.ax.xlabel(self.xlabel) 68 self.ax.ylabel(self.ylabel) 69 except AttributeError: 70 self.ax.set_xscale("log", **base_arg) 71 self.ax.set_xlabel(self.xlabel) 72 self.ax.set_ylabel(self.ylabel) 73 if self.legend: 74 self.ax.legend() 75 76 def show(self): 77 self.finish() 78 pyplot.tight_layout() 79 show_plot() 80 81 def save(self, file): 82 self.finish() 83 pyplot.savefig(file, format=self.format) 84 85 def plot(self, spectrum, t): 86 error = self.plot_analytic_solution(self.settings, t, spectrum) 87 self.plot_data(self.settings, t, spectrum) 88 return error 89 90 def plot_analytic_solution(self, settings, t, spectrum=None): 91 if t == 0: 92 analytic_solution = settings.spectrum.size_distribution 93 else: 94 95 def analytic_solution(x): 96 return settings.norm_factor * settings.kernel.analytic_solution( 97 x=x, t=t, x_0=settings.X0, N_0=settings.n_part 98 ) 99 100 volume_bins_edges = self.settings.formulae.trivia.volume( 101 settings.radius_bins_edges 102 ) 103 dm = np.diff(volume_bins_edges) 104 dr = np.diff(settings.radius_bins_edges) 105 106 pdf_m_x = volume_bins_edges[:-1] + dm / 2 107 pdf_m_y = analytic_solution(pdf_m_x) 108 109 pdf_r_x = settings.radius_bins_edges[:-1] + dr / 2 110 pdf_r_y = pdf_m_y * dm / dr * pdf_r_x 111 112 x = pdf_r_x * si.metres / si.micrometres 113 y_true = ( 114 pdf_r_y 115 * self.settings.formulae.trivia.volume(radius=pdf_r_x) 116 * settings.rho 117 / settings.dv 118 * si.kilograms 119 / si.grams 120 ) 121 122 self.ax.plot(x, y_true, color="black") 123 124 if spectrum is not None: 125 y = spectrum * si.kilograms / si.grams 126 error = error_measure(y, y_true, x) 127 self.title = f"error measure: {error:.2f}" # TODO #327 relative error 128 return error 129 return None 130 131 def plot_data(self, settings, t, spectrum): 132 if self.smooth: 133 scope = self.smooth_scope 134 if t != 0: 135 new = np.copy(spectrum) 136 for _ in range(2): 137 for i in range(scope, len(spectrum) - scope): 138 new[i] = np.mean(spectrum[i - scope : i + scope + 1]) 139 scope = 1 140 for i in range(scope, len(spectrum) - scope): 141 spectrum[i] = np.mean(new[i - scope : i + scope + 1]) 142 143 x = settings.radius_bins_edges[:-scope] 144 dx = np.diff(x) 145 self.ax.plot( 146 (x[:-1] + dx / 2) * si.metres / si.micrometres, 147 spectrum[:-scope] * si.kilograms / si.grams, 148 label=f"t = {t}s", 149 color=self.colors( 150 t / (self.settings.output_steps[-1] * self.settings.dt) 151 ), 152 ) 153 else: 154 self.ax.step( 155 settings.radius_bins_edges[:-1] * si.metres / si.micrometres, 156 spectrum * si.kilograms / si.grams, 157 where="post", 158 label=f"t = {t}s", 159 color=self.colors( 160 t / (self.settings.output_steps[-1] * self.settings.dt) 161 ), 162 )
SpectrumPlotter(settings, title=None, grid=True, legend=True, log_base=10)
31 def __init__(self, settings, title=None, grid=True, legend=True, log_base=10): 32 self.settings = settings 33 self.format = "pdf" 34 self.colors = SpectrumColors() 35 self.smooth = False 36 self.smooth_scope = 2 37 self.legend = legend 38 self.grid = grid 39 self.title = title 40 self.xlabel = "particle radius [µm]" 41 self.ylabel = "dm/dlnr [g/m^3/(unit dr/r)]" 42 self.log_base = log_base 43 self.ax = pyplot 44 self.fig = pyplot 45 self.finished = False
def
finish(self):
47 def finish(self): 48 if self.finished: 49 return 50 self.finished = True 51 if self.grid: 52 self.ax.grid() 53 54 base_arg = { 55 "base" 56 + ( 57 "x" if _matplotlib_version_actual < _matplotlib_version_3_3_3 else "" 58 ): self.log_base 59 } 60 if self.title is not None: 61 try: 62 self.ax.title(self.title) 63 except TypeError: 64 self.ax.set_title(self.title) 65 try: 66 self.ax.xscale("log", **base_arg) 67 self.ax.xlabel(self.xlabel) 68 self.ax.ylabel(self.ylabel) 69 except AttributeError: 70 self.ax.set_xscale("log", **base_arg) 71 self.ax.set_xlabel(self.xlabel) 72 self.ax.set_ylabel(self.ylabel) 73 if self.legend: 74 self.ax.legend()
def
plot_analytic_solution(self, settings, t, spectrum=None):
90 def plot_analytic_solution(self, settings, t, spectrum=None): 91 if t == 0: 92 analytic_solution = settings.spectrum.size_distribution 93 else: 94 95 def analytic_solution(x): 96 return settings.norm_factor * settings.kernel.analytic_solution( 97 x=x, t=t, x_0=settings.X0, N_0=settings.n_part 98 ) 99 100 volume_bins_edges = self.settings.formulae.trivia.volume( 101 settings.radius_bins_edges 102 ) 103 dm = np.diff(volume_bins_edges) 104 dr = np.diff(settings.radius_bins_edges) 105 106 pdf_m_x = volume_bins_edges[:-1] + dm / 2 107 pdf_m_y = analytic_solution(pdf_m_x) 108 109 pdf_r_x = settings.radius_bins_edges[:-1] + dr / 2 110 pdf_r_y = pdf_m_y * dm / dr * pdf_r_x 111 112 x = pdf_r_x * si.metres / si.micrometres 113 y_true = ( 114 pdf_r_y 115 * self.settings.formulae.trivia.volume(radius=pdf_r_x) 116 * settings.rho 117 / settings.dv 118 * si.kilograms 119 / si.grams 120 ) 121 122 self.ax.plot(x, y_true, color="black") 123 124 if spectrum is not None: 125 y = spectrum * si.kilograms / si.grams 126 error = error_measure(y, y_true, x) 127 self.title = f"error measure: {error:.2f}" # TODO #327 relative error 128 return error 129 return None
def
plot_data(self, settings, t, spectrum):
131 def plot_data(self, settings, t, spectrum): 132 if self.smooth: 133 scope = self.smooth_scope 134 if t != 0: 135 new = np.copy(spectrum) 136 for _ in range(2): 137 for i in range(scope, len(spectrum) - scope): 138 new[i] = np.mean(spectrum[i - scope : i + scope + 1]) 139 scope = 1 140 for i in range(scope, len(spectrum) - scope): 141 spectrum[i] = np.mean(new[i - scope : i + scope + 1]) 142 143 x = settings.radius_bins_edges[:-scope] 144 dx = np.diff(x) 145 self.ax.plot( 146 (x[:-1] + dx / 2) * si.metres / si.micrometres, 147 spectrum[:-scope] * si.kilograms / si.grams, 148 label=f"t = {t}s", 149 color=self.colors( 150 t / (self.settings.output_steps[-1] * self.settings.dt) 151 ), 152 ) 153 else: 154 self.ax.step( 155 settings.radius_bins_edges[:-1] * si.metres / si.micrometres, 156 spectrum * si.kilograms / si.grams, 157 where="post", 158 label=f"t = {t}s", 159 color=self.colors( 160 t / (self.settings.output_steps[-1] * self.settings.dt) 161 ), 162 )