diff --git a/S_on_surface_convergence.pgf b/S_on_surface_convergence.pgf new file mode 100644 index 000000000..c7742fd9b --- /dev/null +++ b/S_on_surface_convergence.pgf @@ -0,0 +1,2199 @@ +%% Creator: Matplotlib, PGF backend +%% +%% To include the figure in your LaTeX document, write +%% \input{.pgf} +%% +%% Make sure the required packages are loaded in your preamble +%% \usepackage{pgf} +%% +%% Also ensure that all the required font packages are loaded; for instance, +%% the lmodern package is sometimes necessary when using math font. +%% \usepackage{lmodern} +%% +%% Figures using additional raster images can only be included by \input if +%% they are in the same directory as the main LaTeX file. For loading figures +%% from other directories you can use the `import` package +%% \usepackage{import} +%% +%% and then include the figures with +%% \import{}{.pgf} +%% +%% Matplotlib used the following preamble +%% \def\mathdefault#1{#1} +%% \everymath=\expandafter{\the\everymath\displaystyle} +%% +%% \ifdefined\pdftexversion\else % non-pdftex case. +%% \usepackage{fontspec} +%% \setmainfont{DejaVuSerif.ttf}[Path=\detokenize{/Users/hirish/miniforge3/envs/inteq/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/ttf/}] +%% \setsansfont{DejaVuSans.ttf}[Path=\detokenize{/Users/hirish/miniforge3/envs/inteq/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/ttf/}] +%% \setmonofont{DejaVuSansMono.ttf}[Path=\detokenize{/Users/hirish/miniforge3/envs/inteq/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/ttf/}] +%% \fi +%% \makeatletter\@ifpackageloaded{underscore}{}{\usepackage[strings]{underscore}}\makeatother +%% +\begingroup% +\makeatletter% +\begin{pgfpicture}% +\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{5.348058in}{5.256040in}}% +\pgfusepath{use as bounding box, clip}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetmiterjoin% +\definecolor{currentfill}{rgb}{1.000000,1.000000,1.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.000000pt}% +\definecolor{currentstroke}{rgb}{1.000000,1.000000,1.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{5.348058in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{5.348058in}{5.256040in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{5.256040in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathclose% +\pgfusepath{fill}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetmiterjoin% +\definecolor{currentfill}{rgb}{1.000000,1.000000,1.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.000000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetstrokeopacity{0.000000}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{0.630556in}{0.426079in}}% +\pgfpathlineto{\pgfqpoint{5.280556in}{0.426079in}}% +\pgfpathlineto{\pgfqpoint{5.280556in}{5.046079in}}% +\pgfpathlineto{\pgfqpoint{0.630556in}{5.046079in}}% +\pgfpathlineto{\pgfqpoint{0.630556in}{0.426079in}}% +\pgfpathclose% +\pgfusepath{fill}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfpathrectangle{\pgfqpoint{0.630556in}{0.426079in}}{\pgfqpoint{4.650000in}{4.620000in}}% +\pgfusepath{clip}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.121569,0.466667,0.705882}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{1.003750pt}% +\definecolor{currentstroke}{rgb}{0.121569,0.466667,0.705882}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.041667in}{-0.041667in}}{\pgfqpoint{0.041667in}{0.041667in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{-0.041667in}}% +\pgfpathcurveto{\pgfqpoint{0.011050in}{-0.041667in}}{\pgfqpoint{0.021649in}{-0.037276in}}{\pgfqpoint{0.029463in}{-0.029463in}}% +\pgfpathcurveto{\pgfqpoint{0.037276in}{-0.021649in}}{\pgfqpoint{0.041667in}{-0.011050in}}{\pgfqpoint{0.041667in}{0.000000in}}% +\pgfpathcurveto{\pgfqpoint{0.041667in}{0.011050in}}{\pgfqpoint{0.037276in}{0.021649in}}{\pgfqpoint{0.029463in}{0.029463in}}% +\pgfpathcurveto{\pgfqpoint{0.021649in}{0.037276in}}{\pgfqpoint{0.011050in}{0.041667in}}{\pgfqpoint{0.000000in}{0.041667in}}% +\pgfpathcurveto{\pgfqpoint{-0.011050in}{0.041667in}}{\pgfqpoint{-0.021649in}{0.037276in}}{\pgfqpoint{-0.029463in}{0.029463in}}% +\pgfpathcurveto{\pgfqpoint{-0.037276in}{0.021649in}}{\pgfqpoint{-0.041667in}{0.011050in}}{\pgfqpoint{-0.041667in}{0.000000in}}% +\pgfpathcurveto{\pgfqpoint{-0.041667in}{-0.011050in}}{\pgfqpoint{-0.037276in}{-0.021649in}}{\pgfqpoint{-0.029463in}{-0.029463in}}% +\pgfpathcurveto{\pgfqpoint{-0.021649in}{-0.037276in}}{\pgfqpoint{-0.011050in}{-0.041667in}}{\pgfqpoint{0.000000in}{-0.041667in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.041667in}}% +\pgfpathclose% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{5.069192in}{0.896714in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.540783in}{1.340489in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.012374in}{1.895654in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{3.483965in}{2.417790in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.955556in}{2.713141in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.427147in}{3.414062in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.898738in}{3.720859in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.370329in}{4.175250in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{0.841919in}{4.546112in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{5.069192in}{0.909333in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.540783in}{1.473210in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.012374in}{1.897764in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{3.483965in}{2.425534in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.955556in}{2.820005in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.427147in}{3.361493in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.898738in}{3.771106in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.370329in}{4.098554in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{0.841919in}{4.546112in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{5.069192in}{0.636079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.540783in}{1.413294in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.012374in}{1.670896in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{3.483965in}{2.315912in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.955556in}{2.721438in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.427147in}{3.357033in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.898738in}{3.847290in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.370329in}{4.341233in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{0.841919in}{4.836079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{5.069192in}{0.844233in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.540783in}{1.441662in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.012374in}{1.982326in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{3.483965in}{2.254620in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.955556in}{2.813079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.427147in}{2.948417in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.898738in}{3.884878in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.370329in}{4.361491in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{0.841919in}{4.805972in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{5.069192in}{0.877538in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.540783in}{1.394348in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{4.012374in}{1.867478in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{3.483965in}{2.445643in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.955556in}{2.580389in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{2.427147in}{3.240325in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.898738in}{3.790104in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{1.370329in}{4.011749in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsys@transformshift{0.841919in}{4.810555in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.841919in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.841919in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-9}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.370329in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=1.370329in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-8}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.898738in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=1.898738in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-7}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.427147in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=2.427147in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-6}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.955556in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=2.955556in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-5}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.483965in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=3.483965in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-4}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.012374in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=4.012374in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-3}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.540783in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=4.540783in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-2}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{5.069192in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=5.069192in,y=0.328857in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-1}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.631644in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.682852in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.724693in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.760068in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.790711in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.817741in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.000986in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.094035in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.160053in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.211262in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.253102in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.288477in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.319120in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.346150in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.529396in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.622444in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.688463in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.739671in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.781511in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.816886in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.847530in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{1.874559in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.057805in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.150853in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.216872in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.268080in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.309920in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.345295in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.375939in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.402968in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.586214in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.679262in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.745281in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.796489in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.838329in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.873704in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.904348in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.931377in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.114623in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.207671in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.273690in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.324898in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.366738in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.402113in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.432757in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.459786in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.643032in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.736080in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.802099in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.853307in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.895147in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.930522in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.961166in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{3.988195in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.171441in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.264489in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.330508in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.381716in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.423556in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.458932in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.489575in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.516604in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.699850in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.792898in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.858917in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.910125in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.951965in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{4.987341in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{5.017984in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{5.045014in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.602250pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.027778in}}{\pgfqpoint{0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.027778in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{5.228259in}{0.426079in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=2.955556in,y=0.138889in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}Parameter $|x_1|/\overline{x}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.630556in}{1.082331in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.189968in, y=1.029570in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-14}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.630556in}{1.824570in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.189968in, y=1.771809in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-11}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.630556in}{2.566809in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.245331in, y=2.514047in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-8}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.630556in}{3.309048in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.245331in, y=3.256286in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-5}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.630556in}{4.051287in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.245331in, y=3.998525in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{-2}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{% +\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}% +\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{0.630556in}{4.793525in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.332137in, y=4.740764in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}$\mathdefault{10^{1}}$}}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=0.134413in,y=2.736079in,,bottom,rotate=90.000000]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}Relative error (eq. 74)}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfpathrectangle{\pgfqpoint{0.630556in}{0.426079in}}{\pgfqpoint{4.650000in}{4.620000in}}% +\pgfusepath{clip}% +\pgfsetrectcap% +\pgfsetroundjoin% +\pgfsetlinewidth{1.505625pt}% +\definecolor{currentstroke}{rgb}{1.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{5.069192in}{0.889339in}}% +\pgfpathlineto{\pgfqpoint{4.540783in}{1.366545in}}% +\pgfpathlineto{\pgfqpoint{4.012374in}{1.843750in}}% +\pgfpathlineto{\pgfqpoint{3.483965in}{2.320956in}}% +\pgfpathlineto{\pgfqpoint{2.955556in}{2.798161in}}% +\pgfpathlineto{\pgfqpoint{2.427147in}{3.275366in}}% +\pgfpathlineto{\pgfqpoint{1.898738in}{3.752572in}}% +\pgfpathlineto{\pgfqpoint{1.370329in}{4.229777in}}% +\pgfpathlineto{\pgfqpoint{0.841919in}{4.706983in}}% +\pgfpathlineto{\pgfqpoint{5.069192in}{0.889339in}}% +\pgfpathlineto{\pgfqpoint{4.540783in}{1.366545in}}% +\pgfpathlineto{\pgfqpoint{4.012374in}{1.843750in}}% +\pgfpathlineto{\pgfqpoint{3.483965in}{2.320956in}}% +\pgfpathlineto{\pgfqpoint{2.955556in}{2.798161in}}% +\pgfpathlineto{\pgfqpoint{2.427147in}{3.275366in}}% +\pgfpathlineto{\pgfqpoint{1.898738in}{3.752572in}}% +\pgfpathlineto{\pgfqpoint{1.370329in}{4.229777in}}% +\pgfpathlineto{\pgfqpoint{0.841919in}{4.706983in}}% +\pgfpathlineto{\pgfqpoint{5.069192in}{0.889339in}}% +\pgfpathlineto{\pgfqpoint{4.540783in}{1.366545in}}% +\pgfpathlineto{\pgfqpoint{4.012374in}{1.843750in}}% +\pgfpathlineto{\pgfqpoint{3.483965in}{2.320956in}}% +\pgfpathlineto{\pgfqpoint{2.955556in}{2.798161in}}% +\pgfpathlineto{\pgfqpoint{2.427147in}{3.275366in}}% +\pgfpathlineto{\pgfqpoint{1.898738in}{3.752572in}}% +\pgfpathlineto{\pgfqpoint{1.370329in}{4.229777in}}% +\pgfpathlineto{\pgfqpoint{0.841919in}{4.706983in}}% +\pgfpathlineto{\pgfqpoint{5.069192in}{0.889339in}}% +\pgfpathlineto{\pgfqpoint{4.540783in}{1.366545in}}% +\pgfpathlineto{\pgfqpoint{4.012374in}{1.843750in}}% +\pgfpathlineto{\pgfqpoint{3.483965in}{2.320956in}}% +\pgfpathlineto{\pgfqpoint{2.955556in}{2.798161in}}% +\pgfpathlineto{\pgfqpoint{2.427147in}{3.275366in}}% +\pgfpathlineto{\pgfqpoint{1.898738in}{3.752572in}}% +\pgfpathlineto{\pgfqpoint{1.370329in}{4.229777in}}% +\pgfpathlineto{\pgfqpoint{0.841919in}{4.706983in}}% +\pgfpathlineto{\pgfqpoint{5.069192in}{0.889339in}}% +\pgfpathlineto{\pgfqpoint{4.540783in}{1.366545in}}% +\pgfpathlineto{\pgfqpoint{4.012374in}{1.843750in}}% +\pgfpathlineto{\pgfqpoint{3.483965in}{2.320956in}}% +\pgfpathlineto{\pgfqpoint{2.955556in}{2.798161in}}% +\pgfpathlineto{\pgfqpoint{2.427147in}{3.275366in}}% +\pgfpathlineto{\pgfqpoint{1.898738in}{3.752572in}}% +\pgfpathlineto{\pgfqpoint{1.370329in}{4.229777in}}% +\pgfpathlineto{\pgfqpoint{0.841919in}{4.706983in}}% +\pgfusepath{stroke}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetrectcap% +\pgfsetmiterjoin% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{0.630556in}{0.426079in}}% +\pgfpathlineto{\pgfqpoint{0.630556in}{5.046079in}}% +\pgfusepath{stroke}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetrectcap% +\pgfsetmiterjoin% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{5.280556in}{0.426079in}}% +\pgfpathlineto{\pgfqpoint{5.280556in}{5.046079in}}% +\pgfusepath{stroke}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetrectcap% +\pgfsetmiterjoin% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{0.630556in}{0.426079in}}% +\pgfpathlineto{\pgfqpoint{5.280556in}{0.426079in}}% +\pgfusepath{stroke}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetrectcap% +\pgfsetmiterjoin% +\pgfsetlinewidth{0.803000pt}% +\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{0.630556in}{5.046079in}}% +\pgfpathlineto{\pgfqpoint{5.280556in}{5.046079in}}% +\pgfusepath{stroke}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=2.955556in,y=5.129413in,,base]{\color{textcolor}{\sffamily\fontsize{12.000000}{14.400000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}Relative error in single recurrence step, Laplace 2D, $n=9$}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetmiterjoin% +\definecolor{currentfill}{rgb}{1.000000,1.000000,1.000000}% +\pgfsetfillcolor{currentfill}% +\pgfsetfillopacity{0.800000}% +\pgfsetlinewidth{1.003750pt}% +\definecolor{currentstroke}{rgb}{0.800000,0.800000,0.800000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetstrokeopacity{0.800000}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{2.007091in}{4.527254in}}% +\pgfpathlineto{\pgfqpoint{5.183334in}{4.527254in}}% +\pgfpathquadraticcurveto{\pgfqpoint{5.211111in}{4.527254in}}{\pgfqpoint{5.211111in}{4.555032in}}% +\pgfpathlineto{\pgfqpoint{5.211111in}{4.948857in}}% +\pgfpathquadraticcurveto{\pgfqpoint{5.211111in}{4.976635in}}{\pgfqpoint{5.183334in}{4.976635in}}% +\pgfpathlineto{\pgfqpoint{2.007091in}{4.976635in}}% +\pgfpathquadraticcurveto{\pgfqpoint{1.979313in}{4.976635in}}{\pgfqpoint{1.979313in}{4.948857in}}% +\pgfpathlineto{\pgfqpoint{1.979313in}{4.555032in}}% +\pgfpathquadraticcurveto{\pgfqpoint{1.979313in}{4.527254in}}{\pgfqpoint{2.007091in}{4.527254in}}% +\pgfpathlineto{\pgfqpoint{2.007091in}{4.527254in}}% +\pgfpathclose% +\pgfusepath{stroke,fill}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetbuttcap% +\pgfsetroundjoin% +\definecolor{currentfill}{rgb}{0.121569,0.466667,0.705882}% +\pgfsetfillcolor{currentfill}% +\pgfsetlinewidth{1.003750pt}% +\definecolor{currentstroke}{rgb}{0.121569,0.466667,0.705882}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.041667in}{-0.041667in}}{\pgfqpoint{0.041667in}{0.041667in}}{% +\pgfpathmoveto{\pgfqpoint{0.000000in}{-0.041667in}}% +\pgfpathcurveto{\pgfqpoint{0.011050in}{-0.041667in}}{\pgfqpoint{0.021649in}{-0.037276in}}{\pgfqpoint{0.029463in}{-0.029463in}}% +\pgfpathcurveto{\pgfqpoint{0.037276in}{-0.021649in}}{\pgfqpoint{0.041667in}{-0.011050in}}{\pgfqpoint{0.041667in}{0.000000in}}% +\pgfpathcurveto{\pgfqpoint{0.041667in}{0.011050in}}{\pgfqpoint{0.037276in}{0.021649in}}{\pgfqpoint{0.029463in}{0.029463in}}% +\pgfpathcurveto{\pgfqpoint{0.021649in}{0.037276in}}{\pgfqpoint{0.011050in}{0.041667in}}{\pgfqpoint{0.000000in}{0.041667in}}% +\pgfpathcurveto{\pgfqpoint{-0.011050in}{0.041667in}}{\pgfqpoint{-0.021649in}{0.037276in}}{\pgfqpoint{-0.029463in}{0.029463in}}% +\pgfpathcurveto{\pgfqpoint{-0.037276in}{0.021649in}}{\pgfqpoint{-0.041667in}{0.011050in}}{\pgfqpoint{-0.041667in}{0.000000in}}% +\pgfpathcurveto{\pgfqpoint{-0.041667in}{-0.011050in}}{\pgfqpoint{-0.037276in}{-0.021649in}}{\pgfqpoint{-0.029463in}{-0.029463in}}% +\pgfpathcurveto{\pgfqpoint{-0.021649in}{-0.037276in}}{\pgfqpoint{-0.011050in}{-0.041667in}}{\pgfqpoint{0.000000in}{-0.041667in}}% +\pgfpathlineto{\pgfqpoint{0.000000in}{-0.041667in}}% +\pgfpathclose% +\pgfusepath{stroke,fill}% +}% +\begin{pgfscope}% +\pgfsys@transformshift{2.173758in}{4.852015in}% +\pgfsys@useobject{currentmarker}{}% +\end{pgfscope}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=2.423758in,y=4.815556in,left,base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}Relative Error}}% +\end{pgfscope}% +\begin{pgfscope}% +\pgfsetrectcap% +\pgfsetroundjoin% +\pgfsetlinewidth{1.505625pt}% +\definecolor{currentstroke}{rgb}{1.000000,0.000000,0.000000}% +\pgfsetstrokecolor{currentstroke}% +\pgfsetdash{}{0pt}% +\pgfpathmoveto{\pgfqpoint{2.034869in}{4.660310in}}% +\pgfpathlineto{\pgfqpoint{2.173758in}{4.660310in}}% +\pgfpathlineto{\pgfqpoint{2.312647in}{4.660310in}}% +\pgfusepath{stroke}% +\end{pgfscope}% +\begin{pgfscope}% +\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}% +\pgfsetstrokecolor{textcolor}% +\pgfsetfillcolor{textcolor}% +\pgftext[x=2.423758in,y=4.611699in,left,base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}Linear Least Squares Fit Slope: -1.9673}}% +\end{pgfscope}% +\end{pgfpicture}% +\makeatother% +\endgroup% diff --git a/doc/expansion.rst b/doc/expansion.rst index 5d72d735a..ea2680340 100644 --- a/doc/expansion.rst +++ b/doc/expansion.rst @@ -27,3 +27,8 @@ Estimating Expansion Orders --------------------------- .. automodule:: sumpy.expansion.level_to_order + +Recurrences +----------- + +.. automodule:: sumpy.recurrence diff --git a/output.png b/output.png new file mode 100644 index 000000000..ff4bab9ea Binary files /dev/null and b/output.png differ diff --git a/qbxrecurrence.svg b/qbxrecurrence.svg new file mode 100644 index 000000000..39becc7bc --- /dev/null +++ b/qbxrecurrence.svg @@ -0,0 +1,2098 @@ + + + + + + + + 2025-07-11T22:39:12.289385 + image/svg+xml + + + Matplotlib v3.9.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sumpy/recurrence.py b/sumpy/recurrence.py new file mode 100644 index 000000000..5f29bd454 --- /dev/null +++ b/sumpy/recurrence.py @@ -0,0 +1,505 @@ +r""" +With the functionality in this module, we aim to compute a recurrence for +one-dimensional derivatives of functions :math:`f:\mathbb R^n \to \mathbb R` +for functions satisfying two assumptions: + +- :math:`f` satisfies a PDE that is linear and has coefficients polynomial + in the coordinates. +- :math:`f` only depends on the radius :math:`r`, + i.e. :math:`f(\boldsymbol x)=f(|\boldsymbol x|_2)`. + +This process proceeds in multiple steps: + +- Convert from the PDE to an ODE in :math:`r`, using :func:`pde_to_ode_in_r`. +- Convert from an ODE in :math:`r` to one in :math:`x`, +using :func:`ode_in_r_to_x`. +- Sort general-form ODE in :math:`x` into a coefficient array, using + :func:`ode_in_x_to_coeff_array`. +- Finally, get an expression for the recurrence, using + :func:`recurrence_from_coeff_array`. + +The whole process can be automated using :func:`recurrence_from_pde`. + +.. autofunction:: pde_to_ode_in_r +.. autofunction:: ode_in_r_to_x +.. autofunction:: ode_in_x_to_coeff_array +.. autofunction:: recurrence_from_coeff_array +.. autofunction:: recurrence_from_pde +.. autofunction:: reindex_recurrence_relation +.. autofunction:: move_center_origin_source_arbitrary +.. autofunction:: get_reindexed_and_center_origin_recurrence +""" + +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +import math +from typing import TypeVar + +import numpy as np +import sympy as sp + +from pytools.obj_array import make_obj_array + +from sumpy.expansion.diff_op import ( + DerivativeIdentifier, + LinearPDESystemOperator, +) + + +# similar to make_sym_vector in sumpy.symbolic, but returns an object array +# instead of a sympy.Matrix. +def _make_sympy_vec(name, n): + return make_obj_array([sp.Symbol(f"{name}{i}") for i in range(n)]) + + +def pde_to_ode_in_r(pde: LinearPDESystemOperator) -> tuple[ + sp.Expr, np.ndarray, int +]: + r""" + Returns an ODE satisfied by the radial derivatives of a function + :math:`f:\mathbb R^n \to \mathbb R` satisfying + :math:`f(\boldsymbol x)=f(|\boldsymbol x|_2)` and *pde*. + + :arg pde: must satisfy ``pde.eqs == 1`` and have polynomial coefficients. + + :returns: a tuple ``(ode_in_r, var, ode_order)``, where + - *ode_in_r* with derivatives given as :class:`sympy.Derivative` + - *var* is an object array of :class:`sympy.Symbol`, with successive + variables + representing the Cartesian coordinate directions. + - *ode_order* the order of ODE that is returned + """ + if len(pde.eqs) != 1: + raise ValueError("PDE must be scalar") + + dim = pde.dim + ode_order = pde.order + pde_eqn, = pde.eqs + + var = _make_sympy_vec("x", dim) + r = sp.sqrt(sum(var**2)) + eps = sp.symbols("epsilon") + rval = r + eps + f = sp.Function("f") + + def apply_deriv_id(expr: sp.Expr, + deriv_id: DerivativeIdentifier) -> sp.Expr: + for i, nderivs in enumerate(deriv_id.mi): + expr = expr.diff(var[i], nderivs) + return expr + + ode_in_r = sum( + # pylint: disable-next=not-callable + coeff * apply_deriv_id(f(rval), deriv_id) + for deriv_id, coeff in pde_eqn.items() + ) + + f_r_derivs = _make_sympy_vec("f_r", ode_order+1) + # pylint: disable-next=not-callable + f_derivs = [sp.diff(f(rval), eps, i) for i in range(ode_order+1)] + + # PDE ORDER = ODE ORDER + for i in range(ode_order+1): + ode_in_r = ode_in_r.subs(f_derivs[i], f_r_derivs[i]) + + return ode_in_r, var, ode_order + + +def _generate_nd_derivative_relations(var: np.ndarray, ode_order: int) -> dict: + r""" + Using the chain rule outputs a vector that gives in each component + respectively + :math:`[f(r), f'(r), \dots, f^{(ode_order)}(r)]` as a linear combination of + :math:`[f(x), f'(x), \dots, f^{(ode_order)}(x)]` + + :arg var: array of sympy variables math:`[x_0, x_1, \dots]` + :arg ode_order: the order of the ODE that we will be translating + """ + f_r_derivs = _make_sympy_vec("f_r", ode_order+1) + f_x_derivs = _make_sympy_vec("f_x", ode_order+1) + f = sp.Function("f") + eps = sp.symbols("epsilon") + rval = sp.sqrt(sum(var**2)) + eps + # pylint: disable=not-callable + f_derivs_x = [sp.diff(f(rval), var[0], i) for i in range(ode_order+1)] + f_derivs = [sp.diff(f(rval), eps, i) for i in range(ode_order+1)] + # pylint: disable=not-callable + for i in range(len(f_derivs_x)): + for j in range(len(f_derivs)): + f_derivs_x[i] = f_derivs_x[i].subs(f_derivs[j], f_r_derivs[j]) + system = [f_x_derivs[i] - f_derivs_x[i] for i in range(ode_order+1)] + return sp.solve(system, *f_r_derivs, dict=True)[0] + + +def ode_in_r_to_x(ode_in_r: sp.Expr, var: np.ndarray, + ode_order: int) -> sp.Expr: + r""" + Translates an ode in the variable r into an ode in the variable x + by replacing the terms :math:`f, f_r, f_{rr}, \dots` as a linear + combinations of + :math:`f, f_x, f_{xx}, \dots` using the chain rule. + + :arg ode_in_r: a linear combination of :math:`f, f_r, f_{rr}, \dots` + represented by the sympy variables :math:`f_{r0}, f_{r1}, f_{r2}, \dots` + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + :arg ode_order: the order of the input ODE + + :returns: *ode_in_x* a linear combination of :math:`f, f_x, f_{xx}, \dots` + represented by the sympy variables :math:`f_{x0}, f_{x1}, f_{x2}, + \dots` with coefficients as rational functions in + :math:`x_0, x_1, \dots` + """ + subme = _generate_nd_derivative_relations(var, ode_order+1) + ode_in_x = ode_in_r + f_r_derivs = _make_sympy_vec("f_r", ode_order+1) + for i in range(ode_order+1): + ode_in_x = ode_in_x.subs(f_r_derivs[i], subme[f_r_derivs[i]]) + return ode_in_x + + +ODECoefficients = list[list[sp.Expr]] + + +def ode_in_x_to_coeff_array(poly: sp.Poly, ode_order: int, var: + np.ndarray) -> ODECoefficients: + r""" + Organizes the coefficients of an ODE in the :math:`x_0` variable into a + 2D array. + + :arg poly: a sympy polynomial in + :math:`\partial_{x_0}^0 f, \partial_{x_0}^1 f,\cdots` of the form + :math:`(b_{00} x_0^0 + b_{01} x_0^1 + \cdots) \partial_{x_0}^0 f + + (b_{10} x_0^0 + b_{11} x_0^1 +\cdots) \partial_x^1 f` + + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + :arg ode_order: the order of the input ODE we return a sequence + + :returns: *coeffs* a sequence of of sequences, with the outer sequence + iterating over derivative orders, and each inner sequence iterating + over powers of :math:`x_0`, so that, in terms of the above form, + coeffs is :math:`[[b_{00}, b_{01}, ...], [b_{10}, b_{11}, ...], ...]` + """ + return [ + # recast ODE coefficient obtained below as polynomial in x0 + sp.Poly( + # get coefficient of deriv_ind'th derivative + poly.coeff_monomial(poly.gens[deriv_ind]), + + var[0]) + # get poly coefficients in /ascending/ order + .all_coeffs()[::-1] + for deriv_ind in range(ode_order+1)] + + +NumberT = TypeVar("NumberT", int, float, complex) + + +def _falling_factorial(arg: NumberT, num_terms: int) -> NumberT: + result = 1 + for i in range(num_terms): + result = result * (arg - i) + return result + + +def _auto_product_rule_single_term(p: int, m: int, var: np.ndarray) -> sp.Expr: + r""" + We assume that we are given the expression :math:`x_0^p f^(m)(x_0)`. We + then output the nth order derivative of the expression where :math:`n` is + a symbolic variable. + We let :math:`s(i)` represent the ith order derivative of f when + we output the final result. + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + """ + n = sp.symbols("n") + s = sp.Function("s") + + return sum( + # pylint: disable=not-callable + _falling_factorial(n, i) + * math.comb(p, i) * s(n-i+m) * var[0]**(p-i) + for i in range(p+1) + ) + + +def recurrence_from_coeff_array(coeffs: list, var: np.ndarray) -> sp.Expr: + r""" + A function that takes in as input an organized 2D coefficient array (see + above) and outputs a recurrence relation. + + :arg coeffs: a sequence of of sequences, described in + :func:`ode_in_x_to_coeff_array` + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + """ + final_recurrence = 0 + # Outer loop is derivative direction + # Inner is polynomial order of x_0 + for m, _ in enumerate(coeffs): + for p, _ in enumerate(coeffs[m]): + final_recurrence += coeffs[m][p] * _auto_product_rule_single_term( + p, m, var) + return final_recurrence + + +def recurrence_from_pde(pde: LinearPDESystemOperator) -> sp.Expr: + r""" + A function that takes in as input a sympy PDE and outputs a recurrence + relation. + + :arg pde: a :class:`sumpy.expansion.diff_op.LinearSystemPDEOperator` + that must satisfy ``pde.eqs == 1`` and have polynomial coefficients + in the coordinates. + :arg var: array of sympy variables :math:`[x_0, x_1, \dots]` + """ + ode_in_r, var, ode_order = pde_to_ode_in_r(pde) + ode_in_x = ode_in_r_to_x(ode_in_r, var, ode_order).simplify() + ode_in_x_cleared = (ode_in_x * var[0]**(pde.order*2-1)).simplify() + # ode_in_x_cleared shouldn't have rational function coefficients + assert sp.together(ode_in_x_cleared) == ode_in_x_cleared + f_x_derivs = _make_sympy_vec("f_x", ode_order+1) + poly = sp.Poly(ode_in_x_cleared, *f_x_derivs) + coeffs = ode_in_x_to_coeff_array(poly, ode_order, var) + return recurrence_from_coeff_array(coeffs, var) + + +def reindex_recurrence_relation(r: sp.Expr) -> tuple[int, sp.Expr]: + r""" + A function that takes in as input a recurrence and outputs a recurrence + relation that has the nth term in terms of the n-1th, n-2th etc. + Also returns the order of the recurrence relation. + + :arg recurrence: a recurrence relation in :math:`s(n)` + """ + idx_l, terms = _extract_idx_terms_from_recurrence(r) + # Order is the max difference between highest/lowest in idx_l + order = max(idx_l) - min(idx_l) + + # How much do we need to shift the recurrence relation + shift_idx = max(idx_l) + + # Get the respective coefficients in the recurrence relation from r + n = sp.symbols("n") + coeffs = sp.poly(r, list(terms)).coeffs() + + # Re-arrange the recurrence relation so we get s(n) = ____ + # in terms of s(n-1), ... + true_recurrence = sum(sp.cancel(-coeffs[i]/coeffs[-1]) * terms[i] + for i in range(0, len(terms)-1)) + true_recurrence1 = true_recurrence.subs(n, n-shift_idx) + + return order, true_recurrence1 + + +def _extract_idx_terms_from_recurrence(r: sp.Expr) -> tuple[np.ndarray, + np.ndarray]: + r""" + Given a recurrence extracts the variables in the recurrence + as well as the indexes, both in sorted order. + + :arg r: recurrence to extract terms from + """ + # We're assuming here that s(...) are the only function calls. + terms = list(r.atoms(sp.Function)) + terms = np.array(terms) + + idx_l = [] + for i in range(len(terms)): + tms = list(terms[i].atoms(sp.Number)) + if len(tms) == 1: + idx_l.append(tms[0]) + else: + idx_l.append(0) + idx_l = np.array(idx_l, dtype="int") + idx_sort = idx_l.argsort() + idx_l = idx_l[idx_sort] + terms = terms[idx_sort] + + return idx_l, terms + + +def _check_neg_ind(r_n): + r""" + Simply checks if a negative index exists in a recurrence relation. + """ + + idx_l, _ = _extract_idx_terms_from_recurrence(r_n) + + return np.any(idx_l < 0) + + +def _get_initial_order_on_axis(recurrence): + r""" + For a on-axis recurrence checks how many initial conditions by + checking for non-negative indexed terms. + """ + n = sp.symbols("n") + + i = 0 + r_c = recurrence.subs(n, i) + while _check_neg_ind(r_c): + i += 1 + r_c = recurrence.subs(n, i) + return i + +def _get_initial_order_off_axis(recurrence): + r""" + For a off-axis recurrence checks how many initial conditions by + checking for non-negative indexed terms. + """ + n = sp.symbols("n") + + i = 0 + r_c = recurrence.subs(n, i) + while (_check_neg_ind(r_c) or r_c == 0) or i % 2 != 0: + i += 1 + r_c = recurrence.subs(n, i) + return i + + +def move_center_origin_source_arbitrary(r: sp.Expr) -> sp.Expr: + r""" + A function that "shifts" a recurrence so it's center is placed + at the origin and source is the input for the recurrence generated. + Assuming the recurrence is formulated so that evaluating it gives + s(n) in terms of s(n-1), .., etc. We do NOT want a recurrence + EXPRESSION as input, i.e. an expression containing s(n), s(n-1), + ..., that evaluates to 0. + Use move_center_origin_source_arbitrary_expression for EXPRESSIONS. + + :arg recurrence: a recurrence relation in :math:`s(n)` + """ + idx_l, terms = _extract_idx_terms_from_recurrence(r) + + r_ret = r + + n = sp.symbols("n") + for i in range(len(idx_l)): + r_ret = r_ret.subs(terms[i], (-1)**(n+idx_l[i])*terms[i]) + + return r_ret*((-1)**(n)) + + +def get_reindexed_and_center_origin_on_axis_recurrence(pde) -> tuple[int, int, + sp.Expr]: + r""" + A function that "shifts" the recurrence so the expansion center is placed + at the origin and source is the input for the recurrence generated. + + Also processes the recurrence so s(n) is in terms of s(n-1), etc. + + :arg recurrence: a recurrence relation in :math:`s(n)` + + :returns: a tuple ``(n_initial, order, r_s)``, where + - *n_initial* is the number of initial derivatives needed + - *order* is the order of the recurrence r_s + - *r_s* is the shifted/processed recurrence + """ + r = recurrence_from_pde(pde) + order, r_p = reindex_recurrence_relation(r) + n_initial = _get_initial_order_on_axis(r_p) + r_s = move_center_origin_source_arbitrary(r_p) + return n_initial, order, r_s + +# ================ OFF-AXIS RECURRENCE ================= +def _move_center_origin_source_arbitrary_expression(pde: LinearPDESystemOperator) -> sp.Expr: + r""" + A function that "shifts" the recurrence so it's center is placed + at the origin and source is the input for the recurrence generated. + Outputs an expression that evaluates to 0 rather than s(n) in terms + of s(n-1), etc. This is different from move_center_origin_source_arbitrary, + because we are "shifting" an EXPRESSION, not s(n) in terms of s(n-1), etc. + + :arg recurrence: a recurrence relation in :math:`s(n)` + """ + r = recurrence_from_pde(pde) + + idx_l, terms = _extract_idx_terms_from_recurrence(r) + n = sp.symbols("n") + + r_ret = r + for i in range(len(idx_l)): + r_ret = r_ret.subs(terms[i], (-1)**(n+idx_l[i])*terms[i]) + + return r_ret + + +def get_reindexed_and_center_origin_off_axis_recurrence(pde: LinearPDESystemOperator) -> [int, int, sp.Expr]: + r""" + A function that takes in as input a pde and outputs a off-axis recurrence + for derivatives taken at the origin with an arbitrary source location. + The recurrence is reindexed so it gives s(n) in terms of s(n-1), ..., etc. + """ + var = _make_sympy_vec("x", 1) + r_exp = _move_center_origin_source_arbitrary_expression(pde).subs(var[0], 0) + recur_order, recur = reindex_recurrence_relation(r_exp) + start_order = _get_initial_order_off_axis(recur) + return start_order, recur_order, recur + + +def get_off_axis_expression(pde, taylor_order=4) -> [sp.Expr, int]: + r""" + A function that takes in as input a pde, and outputs + the Taylor expression that gives the n th derivative + as a truncated taylor_order th order Taylor series with respect to x_0 and + s(i) where s(i) comes from the off-axis recurrence. See + get_reindexed_and_center_origin_off_axis_recurrence. + + Also outputs the number of coefficients it needs from nth order. + So if it outputs 3 as the second return value, then it needs + s(deriv_order), s(deriv_order-1), ..., s(deriv_order-3). + + YOU CANNOT SUB N < START_ORDER INTO THE OUTPUTTED EXPRESSION. + I CANNOT REARRANGE THE EXPRESSION IN THIS CASE TO HAVE INDICES + LOWER THAN THE SUBSITUTED N VALUE. + """ + s = sp.Function("s") + n = sp.symbols("n") + deriv_order = n + + start_order, _, t_recurrence = get_reindexed_and_center_origin_off_axis_recurrence(pde) + var = _make_sympy_vec("x", 2) + exp = 0 + for i in range(taylor_order+1): + exp += s(deriv_order+i)/math.factorial(i) * var[0]**i + + #While derivatives w/order larger than the deriv_order exist in our taylor expression + #replace them with smaller order derivatives + + idx_l, _ = _extract_idx_terms_from_recurrence(exp) + max_idx = max(idx_l) + + while max_idx > 0: + for ind in idx_l: + if ind > 0: + exp = exp.subs(s(n+ind), t_recurrence.subs(n, n+ind)) + + idx_l, _ = _extract_idx_terms_from_recurrence(exp) + max_idx = max(idx_l) + + idx_l, _ = _extract_idx_terms_from_recurrence(exp) + + return exp*(-1)**n, -min(idx_l), start_order \ No newline at end of file diff --git a/sumpy/recurrence_qbx.py b/sumpy/recurrence_qbx.py new file mode 100644 index 000000000..842244aca --- /dev/null +++ b/sumpy/recurrence_qbx.py @@ -0,0 +1,276 @@ +r""" +With the functionality in this module, we compute layer potentials +using a recurrence for one-dimensional derivatives of the corresponding +Green's function. See recurrence.py. + +.. autofunction:: recurrence_qbx_lp +""" +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import math +from typing import Sequence + +import numpy as np +import sympy as sp + +from sumpy.recurrence import ( + _make_sympy_vec, + get_off_axis_expression, + get_reindexed_and_center_origin_off_axis_recurrence, + get_reindexed_and_center_origin_on_axis_recurrence, +) + + +# ================ Transform/Rotate ================= +def _produce_orthogonal_basis(normals: np.ndarray) -> Sequence[np.ndarray]: + ndim, ncenters = normals.shape + orth_coordsys = [normals] + for i in range(1, ndim): + v = np.random.rand(ndim, ncenters) # noqa: NPY002 + v = v/np.linalg.norm(v, 2, axis=0) + for j in range(i): + v = v - np.einsum("dc,dc->c", v, + orth_coordsys[j]) * orth_coordsys[j] + v = v/np.linalg.norm(v, 2, axis=0) + orth_coordsys.append(v) + + return orth_coordsys + + +def _compute_rotated_shifted_coordinates( + sources: np.ndarray, + centers: np.ndarray, + normals: np.ndarray + ) -> np.ndarray: + cts = sources[:, None] - centers[:, :, None] + orth_coordsys = _produce_orthogonal_basis(normals) + cts_rotated_shifted = np.einsum("idc,dcs->ics", orth_coordsys, cts) + + return cts_rotated_shifted + + +# ================ Recurrence LP Eval ================= +def recurrence_qbx_lp(sources, centers, normals, strengths, radius, pde, g_x_y, + ndim, p, off_axis_start=0) -> np.ndarray: + r""" + A function that computes a single-layer potential using a recurrence. + + :arg sources: a (ndim, nsources) array of source locations + :arg centers: a (ndim, ncenters) array of center locations + :arg normals: a (ndim, ncenters) array of normals + :arg strengths: array corresponding to quadrature weight multiplied by + density + :arg radius: expansion radius + :arg pde: pde that we are computing layer potential for + :arg g_x_y: a green's function in (x0, x1, ...) source and + (t0, t1, ...) target + :arg ndim: number of spatial variables + :arg p: order of expansion computed + """ + + # ------------- 2. Compute rotated/shifted coordinates + cts_r_s = _compute_rotated_shifted_coordinates(sources, centers, normals) + + # ------------- 4. Define input variables for green's function expression + var = _make_sympy_vec("x", ndim) + var_t = _make_sympy_vec("t", ndim) + + # ------------ 5. Compute recurrence + n_initial, order, recurrence = get_reindexed_and_center_origin_on_axis_recurrence(pde) + + # ------------ 6. Set order p = 5 + n_p = sources.shape[1] + storage = [np.zeros((n_p, n_p))] * order + + s = sp.Function("s") + n = sp.symbols("n") + + def generate_lamb_expr(i, n_initial): + arg_list = [] + for j in range(order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < n_initial: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = recurrence.subs(n, i) + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb)#, sp.lambdify(arg_list, lamb_expr_symb_deriv) + + + coord = [cts_r_s[j] for j in range(ndim)] + interactions_on_axis = coord[0] * 0 + for i in range(p+1): + lamb_expr = generate_lamb_expr(i, n_initial) + a = [*storage, *coord] + s_new = lamb_expr(*a) + + """ + s_new_true = true_lamb_expr(*a) + arg_max = np.argmax(abs(s_new-s_new_true)/abs(s_new_true)) + print((s_new-s_new_true).reshape(-1)[arg_max]/s_new_true.reshape(-1)[arg_max]) + print("x:", coord[0].reshape(-1)[arg_max], "y:", coord[1].reshape(-1)[arg_max], + "s_recur:", s_new.reshape(-1)[arg_max], "s_true:", s_new_true.reshape(-1)[arg_max], "order: ", i) + """ + + interactions_on_axis += s_new * radius**i/math.factorial(i) + + storage.pop(0) + storage.append(s_new) + + + ### NEW CODE - COMPUTE OFF AXIS INTERACTIONS + start_order, t_recur_order, t_recur = get_reindexed_and_center_origin_off_axis_recurrence(pde) + t_exp, t_exp_order, _ = get_off_axis_expression(pde, 8) + storage_taylor_order = max(t_recur_order, t_exp_order+1) + + start_order = max(start_order, order) + + storage_taylor = [np.zeros((n_p, n_p))] * storage_taylor_order + def gen_lamb_expr_t_recur(i, start_order): + arg_list = [] + for j in range(t_recur_order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv.subs(var[0], 0) + else: + lamb_expr_symb = t_recur.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + + def gen_lamb_expr_t_exp(i, t_exp_order, start_order): + arg_list = [] + for j in range(t_exp_order, -1, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = t_exp.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + + interactions_off_axis = 0 + for i in range(p+1): + lamb_expr_t_recur = gen_lamb_expr_t_recur(i, start_order) + a1 = [*storage_taylor[(-t_recur_order):], *coord] + + storage_taylor.pop(0) + storage_taylor.append(lamb_expr_t_recur(*a1) + np.zeros((n_p, n_p))) + + lamb_expr_t_exp = gen_lamb_expr_t_exp(i, t_exp_order, start_order) + a2 = [*storage_taylor[-(t_exp_order+1):], *coord] + + interactions_off_axis += lamb_expr_t_exp(*a2) * radius**i/math.factorial(i) + + ################ + # Compute True Interactions + ''' + storage_taylor_true = [np.zeros((n_p, n_p))] * storage_taylor_order + def generate_true(i): + arg_list = [] + for j in range(ndim): + arg_list.append(var[j]) + + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb)#, sp.lambdify(arg_list, lamb_expr_symb_deriv) + + interactions_true = 0 + for i in range(p+1): + lamb_expr_true = generate_true(i) + a4 = [*coord] + s_new_true = lamb_expr_true(*a4) + interactions_true += s_new_true * radius**i/math.factorial(i) + ''' + ############### + + #slope of line y = mx + m = 100 + mask_on_axis = m*np.abs(coord[0]) >= np.abs(coord[1]) + mask_off_axis = m*np.abs(coord[0]) < np.abs(coord[1]) + + #print("-------------------------") + + #percent_on = np.sum(mask_on_axis)/(mask_on_axis.shape[0]*mask_on_axis.shape[1]) + #percent_off = 1-percent_on + + #relerr_on = np.abs(interactions_on_axis[mask_on_axis]-interactions_true[mask_on_axis])/np.abs(interactions_on_axis[mask_on_axis]) + #print("MAX ON AXIS ERROR(", percent_on, "):", np.max(relerr_on)) + #print(np.mean(relerr_on)) + #print("X:", coord[0][mask_on_axis].reshape(-1)[np.argmax(relerr_on)]) + #print("Y:", coord[1][mask_on_axis].reshape(-1)[np.argmax(relerr_on)]) + + #print("-------------------------") + + #if np.any(mask_off_axis): + # relerr_off = np.abs(interactions_off_axis[mask_off_axis]-interactions_true[mask_off_axis])/np.abs(interactions_off_axis[mask_off_axis]) + # print("MAX OFF AXIS ERROR(", percent_off, "):", np.max(relerr_off)) + # print(np.mean(relerr_off)) + # print("X:", coord[0][mask_off_axis].reshape(-1)[np.argmax(relerr_off)]) + # print("Y:", coord[1][mask_off_axis].reshape(-1)[np.argmax(relerr_off)]) + + interactions_total = np.zeros(coord[0].shape) + interactions_total[mask_on_axis] = interactions_on_axis[mask_on_axis] + interactions_total[mask_off_axis] = interactions_off_axis[mask_off_axis] + + exp_res = (interactions_total * strengths[None, :]).sum(axis=1) + #exp_res_true = (interactions_true * strengths[None, :]).sum(axis=1) + + #relerr_total = np.max(np.abs(exp_res-exp_res_true)/np.abs(exp_res_true)) + #print("OVERALL ERROR:", relerr_total) + + return exp_res \ No newline at end of file diff --git a/sumpy/recurrence_qbx_old.py b/sumpy/recurrence_qbx_old.py new file mode 100644 index 000000000..da00260f1 --- /dev/null +++ b/sumpy/recurrence_qbx_old.py @@ -0,0 +1,147 @@ +r""" +With the functionality in this module, we compute layer potentials +using a recurrence for one-dimensional derivatives of the corresponding +Green's function. See recurrence.py. + +.. autofunction:: recurrence_qbx_lp +""" +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import math +from typing import Sequence + +import numpy as np +import sympy as sp + +from sumpy.recurrence import _make_sympy_vec, get_reindexed_and_center_origin_on_axis_recurrence + + +# ================ Transform/Rotate ================= +def _produce_orthogonal_basis(normals: np.ndarray) -> Sequence[np.ndarray]: + ndim, ncenters = normals.shape + orth_coordsys = [normals] + for i in range(1, ndim): + v = np.random.rand(ndim, ncenters) # noqa: NPY002 + v = v/np.linalg.norm(v, 2, axis=0) + for j in range(i): + v = v - np.einsum("dc,dc->c", v, + orth_coordsys[j]) * orth_coordsys[j] + v = v/np.linalg.norm(v, 2, axis=0) + orth_coordsys.append(v) + + return orth_coordsys + + +def _compute_rotated_shifted_coordinates( + sources: np.ndarray, + centers: np.ndarray, + normals: np.ndarray + ) -> np.ndarray: + cts = sources[:, None] - centers[:, :, None] + orth_coordsys = _produce_orthogonal_basis(normals) + cts_rotated_shifted = np.einsum("idc,dcs->ics", orth_coordsys, cts) + + return cts_rotated_shifted + + +# ================ Recurrence LP Eval ================= +def recurrence_qbx_lp(sources, centers, normals, strengths, radius, pde, g_x_y, + ndim, p) -> np.ndarray: + r""" + A function that computes a single-layer potential using a recurrence. + + :arg sources: a (ndim, nsources) array of source locations + :arg centers: a (ndim, ncenters) array of center locations + :arg normals: a (ndim, ncenters) array of normals + :arg strengths: array corresponding to quadrature weight multiplied by + density + :arg radius: expansion radius + :arg pde: pde that we are computing layer potential for + :arg g_x_y: a green's function in (x0, x1, ...) source and + (t0, t1, ...) target + :arg ndim: number of spatial variables + :arg p: order of expansion computed + """ + + # ------------- 2. Compute rotated/shifted coordinates + cts_r_s = _compute_rotated_shifted_coordinates(sources, centers, normals) + + # ------------- 4. Define input variables for green's function expression + var = _make_sympy_vec("x", ndim) + var_t = _make_sympy_vec("t", ndim) + + # ------------ 5. Compute recurrence + n_initial, order, recurrence = get_reindexed_and_center_origin_on_axis_recurrence(pde) + + # ------------ 6. Set order p = 5 + n_p = sources.shape[1] + storage = [np.zeros((n_p, n_p))] * order + + s = sp.Function("s") + n = sp.symbols("n") + + def generate_lamb_expr(i, n_initial): + arg_list = [] + for j in range(order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + + if i < n_initial: + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = recurrence.subs(n, i) + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb), sp.lambdify(arg_list, lamb_expr_symb_deriv) + + interactions = 0 + coord = [cts_r_s[j] for j in range(ndim)] + for i in range(p+1): + lamb_expr, true_lamb_expr = generate_lamb_expr(i, n_initial) + a = [*storage, *coord] + s_new = lamb_expr(*a) + s_new_true = true_lamb_expr(*a) + arg_max = np.argmax(abs(s_new-s_new_true)/abs(s_new_true)) + print((s_new-s_new_true).reshape(-1)[arg_max]/s_new_true.reshape(-1)[arg_max]) + print("x:", coord[0].reshape(-1)[arg_max], "y:", coord[1].reshape(-1)[arg_max], + "s_recur:", s_new.reshape(-1)[arg_max], "s_true:", s_new_true.reshape(-1)[arg_max], "order: ", i) + interactions += s_new * radius**i/math.factorial(i) + + storage.pop(0) + storage.append(s_new) + + exp_res = (interactions * strengths[None, :]).sum(axis=1) + + return exp_res \ No newline at end of file diff --git a/test/Helmholtz2DCost.svg b/test/Helmholtz2DCost.svg new file mode 100644 index 000000000..489c2391e --- /dev/null +++ b/test/Helmholtz2DCost.svg @@ -0,0 +1,1302 @@ + + + + + + + + 2025-07-12T00:27:20.450262 + image/svg+xml + + + Matplotlib v3.9.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/biharmonic.ipynb b/test/biharmonic.ipynb new file mode 100644 index 000000000..c81c07472 --- /dev/null +++ b/test/biharmonic.ipynb @@ -0,0 +1,148 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import sympy as sp\n", + "\n", + "from sumpy.expansion.diff_op import (\n", + " make_identity_diff_op,\n", + ")\n", + "from collections import namedtuple\n", + "DerivativeIdentifier = namedtuple(\"DerivativeIdentifier\", [\"mi\", \"vec_idx\"])\n", + "\n", + "from sumpy.recurrence import _make_sympy_vec, get_reindexed_and_center_origin_on_axis_recurrence\n", + "\n", + "from immutabledict import immutabledict\n", + "from sumpy.expansion.diff_op import LinearPDESystemOperator\n", + "\n", + "import sympy as sp\n", + "\n", + "from sumpy.recurrence import recurrence_from_coeff_array, ode_in_x_to_coeff_array, pde_to_ode_in_r, ode_in_r_to_x\n", + "\n", + "\n", + "from sumpy.expansion.diff_op import (\n", + " DerivativeIdentifier,\n", + " LinearPDESystemOperator,\n", + " make_identity_diff_op,\n", + " laplacian\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle 3 n x_{0}^{2} s{\\left(n + 1 \\right)} + 3 n x_{0} \\left(n - 1\\right) s{\\left(n \\right)} + 2 n x_{0} s{\\left(n \\right)} + n \\left(n - 2\\right) \\left(n - 1\\right) s{\\left(n - 1 \\right)} + n \\left(n - 1\\right) s{\\left(n - 1 \\right)} + x_{0}^{3} s{\\left(n + 2 \\right)} + x_{0}^{2} s{\\left(n + 1 \\right)} + x_{1}^{2} \\left(n s{\\left(n + 1 \\right)} + x_{0} s{\\left(n + 2 \\right)}\\right) - x_{1}^{2} s{\\left(n + 1 \\right)}$" + ], + "text/plain": [ + "3*n*x0**2*s(n + 1) + 3*n*x0*(n - 1)*s(n) + 2*n*x0*s(n) + n*(n - 2)*(n - 1)*s(n - 1) + n*(n - 1)*s(n - 1) + x0**3*s(n + 2) + x0**2*s(n + 1) + x1**2*(n*s(n + 1) + x0*s(n + 2)) - x1**2*s(n + 1)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w = make_identity_diff_op(2)\n", + "laplace2d = laplacian(w)\n", + "pde = laplace2d\n", + "ode_in_r, var, ode_order = pde_to_ode_in_r(pde)\n", + "ode_in_x = ode_in_r_to_x(ode_in_r, var, ode_order).simplify()\n", + "ode_in_x_cleared = (ode_in_x * var[0]**(pde.order*2-1)).simplify()\n", + "# ode_in_x_cleared shouldn't have rational function coefficients\n", + "assert sp.together(ode_in_x_cleared) == ode_in_x_cleared\n", + "f_x_derivs = _make_sympy_vec(\"f_x\", ode_order+1)\n", + "poly = sp.Poly(ode_in_x_cleared, *f_x_derivs)\n", + "coeffs = ode_in_x_to_coeff_array(poly, ode_order, var)\n", + "recurrence_from_coeff_array(coeffs, var)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle f_{x0} x_{0}^{2}$" + ], + "text/plain": [ + "f_x0*x0**2" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fake_ode_in_x = f_x_derivs[0] * (var[0]**2)\n", + "fake_ode_in_x" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle n^{2} s{\\left(n - 2 \\right)} + 2 n x_{0} s{\\left(n - 1 \\right)} - n s{\\left(n - 2 \\right)} + x_{0}^{2} s{\\left(n \\right)}$" + ], + "text/plain": [ + "n**2*s(n - 2) + 2*n*x0*s(n - 1) - n*s(n - 2) + x0**2*s(n)" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "poly = sp.Poly(fake_ode_in_x, *f_x_derivs)\n", + "coeffs = ode_in_x_to_coeff_array(poly, ode_order, var)\n", + "recurrence_from_coeff_array(coeffs, var).expand()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "inteq", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/count_flops.ipynb b/test/count_flops.ipynb new file mode 100644 index 000000000..2561db66e --- /dev/null +++ b/test/count_flops.ipynb @@ -0,0 +1,549 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from sumpy.recurrence import _make_sympy_vec, get_reindexed_and_center_origin_on_axis_recurrence\n", + "\n", + "from sumpy.expansion.diff_op import (\n", + " laplacian,\n", + " make_identity_diff_op,\n", + ")\n", + "\n", + "import sympy as sp\n", + "from sympy import hankel1\n", + "\n", + "import numpy as np\n", + "import math\n", + "\n", + "from sympy import count_ops\n", + "\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib import cm, ticker\n", + "\n", + "from sympy import cse" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "w = make_identity_diff_op(2)\n", + "helmholtz2d = laplacian(w) + w\n", + "laplace2d = laplacian(w)\n", + "n = sp.symbols(\"n\")\n", + "s = sp.Function(\"s\")\n", + "var = _make_sympy_vec(\"x\", 2)\n", + "n_init_helm, order_helm, recur_helmholtz = get_reindexed_and_center_origin_on_axis_recurrence(helmholtz2d)\n", + "n_init_lap, order_lap, recur_lap = get_reindexed_and_center_origin_on_axis_recurrence(laplace2d)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_derivatives_helmholtz(p):\n", + " var = _make_sympy_vec(\"x\", 2)\n", + " var_t = _make_sympy_vec(\"t\", 2)\n", + " abs_dist = sp.sqrt((var[0]-var_t[0])**2 +\n", + " (var[1]-var_t[1])**2)\n", + " k = 1\n", + " g_x_y = (1j/4) * hankel1(0, k * abs_dist)\n", + " derivs = [sp.diff(g_x_y,\n", + " var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0)\n", + " for i in range(p)]\n", + " return derivs\n", + "l_max_h = 7\n", + "derivs_helmholtz = compute_derivatives_helmholtz(l_max_h)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_derivatives_laplace(p):\n", + " var = _make_sympy_vec(\"x\", 2)\n", + " var_t = _make_sympy_vec(\"t\", 2)\n", + " g_x_y = sp.log(sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2))\n", + " derivs = [sp.diff(g_x_y,\n", + " var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0)\n", + " for i in range(p)]\n", + " return derivs\n", + "l_max_l = 15\n", + "derivs_laplace = compute_derivatives_laplace(l_max_l)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\frac{- x_{0}^{2} + x_{1}^{2}}{\\left(x_{0}^{2} + x_{1}^{2}\\right)^{2}}$" + ], + "text/plain": [ + "(-x0**2 + x1**2)/(x0**2 + x1**2)**2" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "derivs_laplace[2].simplify()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from immutabledict import immutabledict\n", + "from sumpy.expansion.diff_op import LinearPDESystemOperator\n", + "from collections import namedtuple\n", + "DerivativeIdentifier = namedtuple(\"DerivativeIdentifier\", [\"mi\", \"vec_idx\"])\n", + "partial_4x = DerivativeIdentifier((4,0), 0)\n", + "partial_4y = DerivativeIdentifier((0,4), 0)\n", + "partial_2x2y = DerivativeIdentifier((2,2), 0)\n", + "biharmonic_op = {partial_4x: 1, partial_4y: 1, partial_2x2y:2}\n", + "list_pde = immutabledict(biharmonic_op)\n", + "biharmonic_pde = LinearPDESystemOperator(2, (list_pde,))\n", + "n_init_bih, order_bih, recur_bih = get_reindexed_and_center_origin_on_axis_recurrence(biharmonic_pde)\n", + "def compute_derivatives_biharmonic(p):\n", + " var = _make_sympy_vec(\"x\", 2)\n", + " var_t = _make_sympy_vec(\"t\", 2)\n", + " abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2)\n", + " g_x_y = abs_dist**2 * (sp.log(abs_dist))\n", + " derivs = [sp.diff(g_x_y,\n", + " var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0)\n", + " for i in range(p)]\n", + " return derivs\n", + "derivs_bih = compute_derivatives_biharmonic(15)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "n_s = 20\n", + "s_vec = _make_sympy_vec(\"s\", n_s)\n", + "subs_dict_s_vec = dict(zip([s(i) for i in range(n_s)], [s_vec[i] for i in range(n_s)]))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "r = sp.symbols(\"r\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def old_expansion_create(start, stop, derivs):\n", + " return (sum(derivs[i]*r**i/math.factorial(i) for i in range(start, stop)))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def new_expansion_create(start, stop, recur):\n", + " return (sum(recur.subs(n,i)*r**i/math.factorial(i) for i in range(start, stop)))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def get_flops_per_order_lt(l_t_order, derivs, recur, n_init, simplify=True):\n", + " old_exp_flops = []\n", + " new_exp_flops = []\n", + " for i in range(l_t_order):\n", + " if simplify:\n", + " old_exp_flops.append(count_ops(cse((old_expansion_create(0, i+1, derivs)).simplify())))\n", + " new_exp_flops.append(count_ops(cse(((old_expansion_create(0, min(i+1,n_init), derivs) + (new_expansion_create(n_init, i+1, recur)+var[0]*0).subs(subs_dict_s_vec)).simplify()))))\n", + " else:\n", + " if i < 7:\n", + " old_exp_flops.append(count_ops(cse((old_expansion_create(0, i+1, derivs)))))\n", + " new_exp_flops.append(count_ops(cse(((old_expansion_create(0, min(i+1,n_init), derivs) + (new_expansion_create(n_init, i+1, recur)+var[0]*0).subs(subs_dict_s_vec))))))\n", + " else:\n", + " old_exp_flops.append(0)\n", + " new_exp_flops.append(count_ops(cse(((old_expansion_create(0, min(i+1,n_init), derivs) + (new_expansion_create(n_init, i+1, recur)+var[0]*0).subs(subs_dict_s_vec))))))\n", + "\n", + " return old_exp_flops, new_exp_flops" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "old_lap_flops, new_lap_flops = get_flops_per_order_lt(10, derivs_laplace, recur_lap, n_init_lap)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "old_helm_flops, new_helm_flops = get_flops_per_order_lt(15, derivs_helmholtz, recur_helmholtz, n_init_helm, simplify=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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NhNOkcMgIDEKHPlDRpOHzYSrTI4Grsoyq5mIiKiysMnTKGVFLpWgb21H1BjTA7EjliJdVsNoe7qsBmJHLEWNMT3Qt7Yj5FyZlYiIbJBECGHa928j0tLS4OLigtTU1FL1bOR7eAbRpFVHkXMjEarAavAa2AIKmRx9azuingt7NYiIqOooyXcow0YZhA2g6LVR2KNBRERVTUm+Qzlmo4zIpRI0dFehoXvxrk4hIiKyFRyzQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBbFsEFEREQWxbBBREREFsWwQURERBZVJcLGs88+Czc3NwwcONDapRAREVEBVSJsjB07FkuXLrV2GURERGRGlQgbnTp1gpOTk7XLICIiIjOsHjb27NmDvn37wtfXFxKJBGvWrDHZ57vvvkOtWrWgUqnQrFkz7N27t/wLJSIiolKxetjIzMxEWFgY5s+fb3b7ypUrMX78eEyaNAknT55Eu3bt0LNnT8TGxpZzpURERFQacmsX0LNnT/Ts2bPQ7XPmzMErr7yCV199FQAwb948bNmyBQsWLMDMmTNL9Fi5ubnIzc013E5LSytd0URERFRsVu/ZKIpGo8Hx48fRrVs3o/Zu3brhwIEDJT7ezJkz4eLiYvjx9/cvq1KJiIioEBU6bNy9exc6nQ7Vq1c3aq9evToSEhIMt7t3747nn38eGzduhJ+fH44ePWr2eB9++CFSU1MNP3FxcRatn4iIiCrAaZTikEgkRreFEEZt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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize = (6, 6))\n", + "ax.set_yscale(\"log\")\n", + "ax.set_xscale(\"log\")\n", + "ax.plot([i for i in range(15)], new_helm_flops, \n", + " marker='o', # marker type\n", + " markerfacecolor='blue', # color of marker\n", + " markersize=8, # size of marker\n", + " color='skyblue', # color of line\n", + " linewidth=3, # change width of line)\n", + " label='Recurrence'\n", + ")\n", + "ax.plot([i for i in range(7)], old_helm_flops[:7], \n", + " marker='o', # no marker\n", + " color='darkred', # color of line\n", + " linewidth=3, # change width of line\n", + " linestyle='dashed', # change type of line\n", + " label=\"No Recurrence\" # label for legend)\n", + ")\n", + "ax.set_xticks([i+1 for i in range(0,15,2)])\n", + "ax.set_xticklabels(str(i+1) for i in range(0,15,2))\n", + "ax.legend()\n", + "ax.set_title(\"Helmholtz 2D Line-Taylor Flop Comparison\")\n", + "ax.set_ylabel(\"Flop Count\")\n", + "ax.set_xlabel(\"Line-Taylor Order\")\n", + "fig.savefig(\"Helmholtz2DCost.svg\")\n", + "fig.savefig(\"../../S_on_surface_convergence.pgf\", bbox_inches='tight', pad_inches=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], + "source": [ + "expr = var[0]**2 +sp.sin(var[0]**2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle x_{0}^{2} + \\sin{\\left(x_{0}^{2} \\right)}$" + ], + "text/plain": [ + "x0**2 + sin(x0**2)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], + "source": [ + "expr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], + "source": [ + "count_ops(expr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([(x1, x0**2)], [x1 + sin(x1)])" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], + "source": [ + "cse(expr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], + "source": [ + "count_ops(cse(expr))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mnotebook controller is DISPOSED. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "inteq", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/investigate_normal_recurrence copy.ipynb b/test/investigate_normal_recurrence copy.ipynb new file mode 100644 index 000000000..95784f254 --- /dev/null +++ b/test/investigate_normal_recurrence copy.ipynb @@ -0,0 +1,322 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from sumpy.recurrence import _make_sympy_vec, get_reindexed_and_center_origin_on_axis_recurrence, pde_to_ode_in_r, ode_in_r_to_x\n", + "\n", + "from sumpy.expansion.diff_op import (\n", + " laplacian,\n", + " make_identity_diff_op,\n", + ")\n", + "\n", + "\n", + "import sympy as sp\n", + "from sympy import hankel1\n", + "\n", + "import numpy as np\n", + "\n", + "import math\n", + "\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib import cm, ticker" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "w = make_identity_diff_op(2)\n", + "laplace2d = laplacian(w)\n", + "n_init_lap, order_lap, recur_laplace = get_reindexed_and_center_origin_on_axis_recurrence(laplace2d)\n", + "\n", + "w = make_identity_diff_op(2)\n", + "helmholtz2d = laplacian(w) + w\n", + "n_init_helm, order_helm, recur_helmholtz = get_reindexed_and_center_origin_on_axis_recurrence(helmholtz2d)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\frac{f_{r1}}{\\sqrt{x_{0}^{2} + x_{1}^{2}}} + f_{r2}$" + ], + "text/plain": [ + "f_r1/sqrt(x0**2 + x1**2) + f_r2" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pde_to_ode_in_r(laplace2d)[0].simplify()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\frac{f_{x1}}{x_{0}} - \\frac{f_{x1} x_{1}^{2}}{x_{0}^{3}} + f_{x2} + \\frac{f_{x2} x_{1}^{2}}{x_{0}^{2}}$" + ], + "text/plain": [ + "f_x1/x0 - f_x1*x1**2/x0**3 + f_x2 + f_x2*x1**2/x0**2" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ode_in_r_to_x(pde_to_ode_in_r(laplace2d)[0].simplify(), _make_sympy_vec(\"x\",2), 2).simplify()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "var = _make_sympy_vec(\"x\", 2)\n", + "rct = sp.symbols(\"r_{ct}\")\n", + "s = sp.Function(\"s\")\n", + "n = sp.symbols(\"n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_derivatives(p):\n", + " var = _make_sympy_vec(\"x\", 2)\n", + " var_t = _make_sympy_vec(\"t\", 2)\n", + " g_x_y = sp.log(sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2))\n", + " derivs = [sp.diff(g_x_y,\n", + " var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0)\n", + " for i in range(p)]\n", + " return derivs\n", + "l_max = 15\n", + "derivs_laplace = compute_derivatives(l_max)\n", + "derivs_laplace_dict = dict(zip([s(i) for i in range(l_max)], [derivs_laplace[i] for i in range(l_max)]))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_rel_err(nsub, coord_dict):\n", + " return abs((recur_laplace.subs(n, nsub).subs(derivs_laplace_dict).subs(coord_dict) - derivs_laplace[nsub].subs(coord_dict))/derivs_laplace[nsub].subs(coord_dict))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[73104176967965.2, 13206463414066.4, -82734579052863.7]\n", + "ratio: 5.53548476044700 ||||| digits: 0.7431556594638044 |||| pred error: 1e-15\n", + "rel. error: 2.86737427190217e-15\n" + ] + } + ], + "source": [ + "nsub = 14\n", + "coord_dict = {var[0]: 0.1 * np.random.rand(), var[1]: np.random.rand()}\n", + "recur_coeffs_lap = sp.poly(recur_laplace.subs(n, nsub), [s(i) for i in range(nsub - order_lap, nsub)]).coeffs()\n", + "#[i+nsub-order_lap for i in range(len(recur_coeffs_lap))]\n", + "coeffs_sub = [(recur_coeffs_lap[i]*derivs_laplace[i+nsub-order_lap]).subs(coord_dict) for i in range(len(recur_coeffs_lap))]\n", + "print(coeffs_sub)\n", + "ratio = np.abs(coeffs_sub[0]/coeffs_sub[1])\n", + "print(\"ratio: \", ratio, \"||||| digits: \",np.log10(float(ratio)), \"|||| pred error: 1e-\"+str(int(16-np.ceil(np.log10(float(ratio))))))\n", + "print(\"rel. error: \", compute_rel_err(nsub, coord_dict))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "c = sp.symbols(\"c\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "nsub = 9\n", + "coord_dict_var = {var[0]: var[0], var[1]: c*var[0]}\n", + "recur_coeffs_lap_var = sp.poly(recur_laplace.subs(n, nsub), [s(i) for i in range(nsub - order_lap, nsub)]).coeffs()\n", + "#[i+nsub-order_lap for i in range(len(recur_coeffs_lap))]\n", + "coeffs_sub_var = [(recur_coeffs_lap_var[i]*derivs_laplace[i+nsub-order_lap]).subs(coord_dict_var) for i in range(len(recur_coeffs_lap_var))]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\frac{3 \\left(- c^{8} + 14 c^{6} - 14 c^{2} + 1\\right)}{10 \\left(7 c^{6} - 35 c^{4} + 21 c^{2} - 1\\right)}$" + ], + "text/plain": [ + "3*(-c**8 + 14*c**6 - 14*c**2 + 1)/(10*(7*c**6 - 35*c**4 + 21*c**2 - 1))" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(coeffs_sub_var[0]/coeffs_sub_var[1]).subs(var[0], 1).simplify()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def create_list_of_points(param):\n", + " list_of_points = [{var[0]: param, var[1]: 10**(pw) * param} for pw in range(1, 10)]\n", + " return list_of_points" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "long_list_points = []\n", + "n_p = 5\n", + "for i in range(n_p):\n", + " long_list_points += create_list_of_points(np.random.rand())" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "errors = np.array([compute_rel_err(nsub,l) for l in long_list_points], dtype='float')" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "c_errors = np.array([10**(pw) for pw in range(1, 10)]*n_p)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1.99747123, -17.20696956])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bf = np.polyfit(np.log10(c_errors), np.log10(errors),1)\n", + "bf" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize = (6, 6))\n", + "ax.set_xscale(\"log\")\n", + "ax.set_yscale(\"log\")\n", + "ax.scatter(1/c_errors, errors,label='Relative Error')\n", + "ax.plot(1/c_errors, 10**bf[1] * (c_errors**bf[0]), color='r', label = 'Linear Least Squares Fit Slope: -1.9673')\n", + "ax.set_xlabel(\"Parameter $|x_1|/\\overline{x}$\")\n", + "ax.set_ylabel(\"Relative Error (eq. 75)\")\n", + "ax.set_title(\"Relative Error in Single Recurrence Step, Laplace 2D, $n=9$\")\n", + "ax.legend()\n", + "fig.savefig(\"../../S_on_surface_convergence.pgf\", bbox_inches='tight', pad_inches=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "inteq", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/plotting copy.py b/test/plotting copy.py new file mode 100644 index 000000000..9fa8ffb7b --- /dev/null +++ b/test/plotting copy.py @@ -0,0 +1,326 @@ +import numpy as np +import sympy as sp + +from sumpy.recurrence import ( + _make_sympy_vec, + get_off_axis_expression, + get_reindexed_and_center_origin_off_axis_recurrence, + get_reindexed_and_center_origin_on_axis_recurrence, +) + +from sumpy.expansion.diff_op import ( + laplacian, + make_identity_diff_op, +) + +import matplotlib.pyplot as plt +from matplotlib import cm, ticker +from sympy import hankel1 + +from immutabledict import immutabledict +from sumpy.expansion.diff_op import LinearPDESystemOperator + +nmin = -2 +nmax = 3 +n_levels = (nmax-nmin)+1 +levels = 10**np.linspace(nmin, nmax, n_levels) +tcklabels = ["1e"+str(int(np.round(np.log10(i)))) for i in levels] + +def produce_assumption_values(coords, g_x_y, deriv_order): + ndim = 2 + cts_r_s = coords.reshape(2,coords.shape[1],1) + coord = [cts_r_s[j] for j in range(ndim)] + ndim = cts_r_s.shape[0] + var = _make_sympy_vec("x", ndim) + var_t = _make_sympy_vec("t", ndim) + ################ + # Compute True Interactions + def generate_true(i): + arg_list = [] + for j in range(ndim): + arg_list.append(var[j]) + + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb)#, sp.lambdify(arg_list, lamb_expr_symb_deriv) + + interactions_true = 0 + i = deriv_order + lamb_expr_true = generate_true(i) + a4 = [*coord] + s_new_true = lamb_expr_true(*a4) + interactions_true += s_new_true + if deriv_order % 2 == 0: + denom = coord[1]/coord[1]**(deriv_order+1) + else: + denom = coord[0]/coord[1]**(deriv_order+1) + + return np.abs(interactions_true)/denom, coord[1] + ############### + + +def produce_error_for_recurrences(coords, pde, g_x_y, deriv_order, m=100): + + #Possibly reshape coords? + cts_r_s = coords.reshape(2,coords.shape[1],1) + + p = deriv_order-1 + cts_r_s = coords + ndim = cts_r_s.shape[0] + var = _make_sympy_vec("x", ndim) + var_t = _make_sympy_vec("t", ndim) + + # ------------ 5. Compute recurrence + n_initial, order, recurrence = get_reindexed_and_center_origin_on_axis_recurrence(pde) + + # ------------ 6. Set order p = 5 + n_p = cts_r_s.shape[1] + storage = [np.zeros((1, n_p))] * order + + s = sp.Function("s") + n = sp.symbols("n") + + def generate_lamb_expr(i, n_initial): + arg_list = [] + for j in range(order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < n_initial: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = recurrence.subs(n, i) + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb)#, sp.lambdify(arg_list, lamb_expr_symb_deriv) + + interactions_on_axis = 0 + coord = [cts_r_s[j] for j in range(ndim)] + for i in range(p+1): + lamb_expr = generate_lamb_expr(i, n_initial) + a = [*storage, *coord] + s_new = lamb_expr(*a) + + """ + s_new_true = true_lamb_expr(*a) + arg_max = np.argmax(abs(s_new-s_new_true)/abs(s_new_true)) + print((s_new-s_new_true).reshape(-1)[arg_max]/s_new_true.reshape(-1)[arg_max]) + print("x:", coord[0].reshape(-1)[arg_max], "y:", coord[1].reshape(-1)[arg_max], + "s_recur:", s_new.reshape(-1)[arg_max], "s_true:", s_new_true.reshape(-1)[arg_max], "order: ", i) + """ + if i == p: + interactions_on_axis += s_new + + storage.pop(0) + storage.append(s_new) + + + ### NEW CODE - COMPUTE OFF AXIS INTERACTIONS + start_order, t_recur_order, t_recur = get_reindexed_and_center_origin_off_axis_recurrence(pde) + t_exp, t_exp_order, _ = get_off_axis_expression(pde, 8) + storage_taylor_order = max(t_recur_order, t_exp_order+1) + + start_order = max(start_order, order) + + storage_taylor = [np.zeros((1, n_p))] * storage_taylor_order + def gen_lamb_expr_t_recur(i, start_order): + arg_list = [] + for j in range(t_recur_order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv.subs(var[0], 0) + else: + lamb_expr_symb = t_recur.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + + def gen_lamb_expr_t_exp(i, t_exp_order, start_order): + arg_list = [] + for j in range(t_exp_order, -1, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = t_exp.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + + interactions_off_axis = 0 + for i in range(p+1): + lamb_expr_t_recur = gen_lamb_expr_t_recur(i, start_order) + a1 = [*storage_taylor[(-t_recur_order):], *coord] + + storage_taylor.pop(0) + storage_taylor.append(lamb_expr_t_recur(*a1) + np.zeros((1, n_p))) + + lamb_expr_t_exp = gen_lamb_expr_t_exp(i, t_exp_order, start_order) + a2 = [*storage_taylor[-(t_exp_order+1):], *coord] + + if i == p: + interactions_off_axis += lamb_expr_t_exp(*a2) + + ################ + # Compute True Interactions + def generate_true(i): + arg_list = [] + for j in range(ndim): + arg_list.append(var[j]) + + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb)#, sp.lambdify(arg_list, lamb_expr_symb_deriv) + + interactions_true = 0 + for i in range(p, p+1): + lamb_expr_true = generate_true(i) + a4 = [*coord] + s_new_true = lamb_expr_true(*a4) + if i == p: + interactions_true += s_new_true + ############### + + #slope of line y = mx + mask_on_axis = m*np.abs(coord[0]) >= np.abs(coord[1]) + mask_off_axis = m*np.abs(coord[0]) < np.abs(coord[1]) + + interactions_off_axis = interactions_off_axis.reshape(coord[0].shape) + + interactions_total = interactions_on_axis * 0 + interactions_total[mask_on_axis] = interactions_on_axis[mask_on_axis] + interactions_total[mask_off_axis] = interactions_off_axis[mask_off_axis] + + return interactions_on_axis, interactions_off_axis, interactions_true, interactions_total + +def create_logarithmic_mesh(res): + + x_grid = [10**(pw) for pw in np.linspace(-8, 0, res)] + y_grid = [10**(pw) for pw in np.linspace(-8, 0, res)] + + mesh = np.meshgrid(x_grid, y_grid) + mesh_points = np.array(mesh).reshape(2, -1) + + return mesh_points, x_grid, y_grid + +def create_plot(relerr_on, ax, str_title, acbar=True): + cs = ax.contourf(x_grid, y_grid, relerr_on.reshape(res, res), locator=ticker.LogLocator(), cmap=cm.plasma, levels=levels, extend="both") + if acbar: + cbar = fig.colorbar(cs) + cbar.set_ticks(levels) + cbar.set_ticklabels(tcklabels) + + ax.set_xscale('log') + ax.set_yscale('log') + ax.set_xlabel("$x_1$-coordinate", fontsize=15) + ax.set_ylabel("$x_2$-coordinate", fontsize=15) + ax.set_title(str_title) + + return cs + +def create_suite_plot(relerr_on, relerr_off, relerr_comb, str_title): + fig, (ax1,ax2,ax3) = plt.subplots(1, 3, figsize=(15, 8)) + cs = create_plot(relerr_on, ax1, "Laplace 2D (eq. 107)", False) + cs = create_plot(relerr_off, ax2, "Helmholtz 2D (eq. 107)", False) + cs = create_plot(relerr_comb, ax3, "Biharmonic 2D (eq. 109)", False) + + n_levels = 3 + + + fig.subplots_adjust(wspace=0.3, hspace=0.5) + + cbar = fig.colorbar(cs, ax=[ax1,ax2,ax3], shrink=0.9, location='bottom') + cbar.set_ticks(levels) + cbar.set_ticklabels(tcklabels) + fig.suptitle(str_title, fontsize=16) + +#========================= DEFINE PLOT RESOLUTION ==================================== +res = 32 +mesh_points, x_grid, y_grid = create_logarithmic_mesh(res) + +#========================= DEFINE GREEN'S FUNCTIONS/PDE's ==================================== +from collections import namedtuple +DerivativeIdentifier = namedtuple("DerivativeIdentifier", ["mi", "vec_idx"]) +var = _make_sympy_vec("x", 2) +var_t = _make_sympy_vec("t", 2) +abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2) +w = make_identity_diff_op(2) + +partial_4x = DerivativeIdentifier((4,0), 0) +partial_4y = DerivativeIdentifier((0,4), 0) +partial_2x2y = DerivativeIdentifier((2,2), 0) +biharmonic_op = {partial_4x: 1, partial_4y: 1, partial_2x2y:2} +list_pde = immutabledict(biharmonic_op) + +biharmonic_pde = LinearPDESystemOperator(2, (list_pde,)) +g_x_y_biharmonic = abs_dist**2 * sp.log(abs_dist) + +laplace2d = laplacian(w) +g_x_y_laplace = (-1/(2*np.pi)) * sp.log(abs_dist) + +k = 1 +helmholtz2d = laplacian(w) + w +g_x_y_helmholtz = (1j/4) * hankel1(0, k * abs_dist) +#========================= LAPLACE 2D ==================================== +#interactions_on_axis, interactions_off_axis, interactions_true, interactions_total = produce_error_for_recurrences(mesh_points, laplace2d, g_x_y_laplace, 10,m=1e2/2) + +#relerr_on = np.abs((interactions_on_axis-interactions_true)/interactions_true) +#relerr_off = np.abs((interactions_off_axis-interactions_true)/interactions_true) +#relerr_comb = np.abs((interactions_total-interactions_true)/interactions_true) + +#create_suite_plot(relerr_on, relerr_off, relerr_comb, "Laplace 2D: 9th Order Derivative Evaluation Error $(u_{recur}-u_{sympy})/u_{recur}$") + +#========================= HELMOLTZ 2D ==================================== +#interactions_on_axis, interactions_off_axis, interactions_true, interactions_total = produce_error_for_recurrences(mesh_points, helmholtz2d, g_x_y_helmholtz, 8) + +#relerr_on = np.abs((interactions_on_axis-interactions_true)/interactions_true) +#relerr_off = np.abs((interactions_off_axis-interactions_true)/interactions_true) +#relerr_comb = np.abs((interactions_total-interactions_true)/interactions_true) + +#create_suite_plot(relerr_on, relerr_off, relerr_comb, "Helmholtz 2D: 8th Order Derivative Evaluation Error $(u_{recur}-u_{sympy})/u_{recur}$") + + +#======================== BIHARMONIC 2D =================================== +#interactions_on_axis, interactions_off_axis, interactions_true, interactions_total = produce_error_for_recurrences(mesh_points, biharmonic_pde, g_x_y_biharmonic, 12, m=1e2/2) +deriv_order = 6 +relerr_on, _ = produce_assumption_values(mesh_points, g_x_y_laplace, deriv_order) +relerr_off, _ = produce_assumption_values(mesh_points, g_x_y_helmholtz, deriv_order) +relerr_comb, coord1 = produce_assumption_values(mesh_points, g_x_y_biharmonic, deriv_order) + +#relerr_on = np.abs((interactions_on_axis-interactions_true)/interactions_true) +#relerr_off = np.abs((interactions_off_axis-interactions_true)/interactions_true) +#relerr_comb = np.abs((interactions_total-interactions_true)/interactions_true) + +create_suite_plot(relerr_on+1e-20, relerr_off+1e-20, relerr_comb/coord1**2+1e-20, "Asymptotic Behavior of Derivatives ($d="+str(deriv_order)+"$)") +plt.savefig("../S_on_surface_convergence.pgf", bbox_inches='tight', pad_inches=0) +plt.show() \ No newline at end of file diff --git a/test/plotting.py b/test/plotting.py new file mode 100644 index 000000000..90784a17f --- /dev/null +++ b/test/plotting.py @@ -0,0 +1,281 @@ +import numpy as np +import sympy as sp + +from sumpy.recurrence import ( + _make_sympy_vec, + get_off_axis_expression, + get_reindexed_and_center_origin_off_axis_recurrence, + get_reindexed_and_center_origin_on_axis_recurrence, +) + +from sumpy.expansion.diff_op import ( + laplacian, + make_identity_diff_op, +) + +import matplotlib.pyplot as plt +from matplotlib import cm, ticker +from sympy import hankel1 + +from immutabledict import immutabledict +from sumpy.expansion.diff_op import LinearPDESystemOperator + +def produce_error_for_recurrences(coords, pde, g_x_y, deriv_order, m=100): + + #Possibly reshape coords? + cts_r_s = coords.reshape(2,coords.shape[1],1) + + p = deriv_order-1 + cts_r_s = coords + ndim = cts_r_s.shape[0] + var = _make_sympy_vec("x", ndim) + var_t = _make_sympy_vec("t", ndim) + + # ------------ 5. Compute recurrence + n_initial, order, recurrence = get_reindexed_and_center_origin_on_axis_recurrence(pde) + + # ------------ 6. Set order p = 5 + n_p = cts_r_s.shape[1] + storage = [np.zeros((1, n_p))] * order + + s = sp.Function("s") + n = sp.symbols("n") + + def generate_lamb_expr(i, n_initial): + arg_list = [] + for j in range(order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < n_initial: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = recurrence.subs(n, i) + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb)#, sp.lambdify(arg_list, lamb_expr_symb_deriv) + + interactions_on_axis = 0 + coord = [cts_r_s[j] for j in range(ndim)] + for i in range(p+1): + lamb_expr = generate_lamb_expr(i, n_initial) + a = [*storage, *coord] + s_new = lamb_expr(*a) + + """ + s_new_true = true_lamb_expr(*a) + arg_max = np.argmax(abs(s_new-s_new_true)/abs(s_new_true)) + print((s_new-s_new_true).reshape(-1)[arg_max]/s_new_true.reshape(-1)[arg_max]) + print("x:", coord[0].reshape(-1)[arg_max], "y:", coord[1].reshape(-1)[arg_max], + "s_recur:", s_new.reshape(-1)[arg_max], "s_true:", s_new_true.reshape(-1)[arg_max], "order: ", i) + """ + if i == p: + interactions_on_axis += s_new + + storage.pop(0) + storage.append(s_new) + + + ### NEW CODE - COMPUTE OFF AXIS INTERACTIONS + start_order, t_recur_order, t_recur = get_reindexed_and_center_origin_off_axis_recurrence(pde) + t_exp, t_exp_order, _ = get_off_axis_expression(pde, 8) + storage_taylor_order = max(t_recur_order, t_exp_order+1) + + start_order = max(start_order, order) + + storage_taylor = [np.zeros((1, n_p))] * storage_taylor_order + def gen_lamb_expr_t_recur(i, start_order): + arg_list = [] + for j in range(t_recur_order, 0, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv.subs(var[0], 0) + else: + lamb_expr_symb = t_recur.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + + def gen_lamb_expr_t_exp(i, t_exp_order, start_order): + arg_list = [] + for j in range(t_exp_order, -1, -1): + # pylint: disable-next=not-callable + arg_list.append(s(i-j)) + for j in range(ndim): + arg_list.append(var[j]) + + if i < start_order: + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + else: + lamb_expr_symb = t_exp.subs(n, i) + + return sp.lambdify(arg_list, lamb_expr_symb) + + + interactions_off_axis = 0 + for i in range(p+1): + lamb_expr_t_recur = gen_lamb_expr_t_recur(i, start_order) + a1 = [*storage_taylor[(-t_recur_order):], *coord] + + storage_taylor.pop(0) + storage_taylor.append(lamb_expr_t_recur(*a1) + np.zeros((1, n_p))) + + lamb_expr_t_exp = gen_lamb_expr_t_exp(i, t_exp_order, start_order) + a2 = [*storage_taylor[-(t_exp_order+1):], *coord] + + if i == p: + interactions_off_axis += lamb_expr_t_exp(*a2) + + ################ + # Compute True Interactions + def generate_true(i): + arg_list = [] + for j in range(ndim): + arg_list.append(var[j]) + + lamb_expr_symb_deriv = sp.diff(g_x_y, var_t[0], i) + for j in range(ndim): + lamb_expr_symb_deriv = lamb_expr_symb_deriv.subs(var_t[j], 0) + lamb_expr_symb = lamb_expr_symb_deriv + + #print("=============== ORDER = " + str(i)) + #print(lamb_expr_symb) + return sp.lambdify(arg_list, lamb_expr_symb)#, sp.lambdify(arg_list, lamb_expr_symb_deriv) + + interactions_true = 0 + for i in range(p, p+1): + lamb_expr_true = generate_true(i) + a4 = [*coord] + s_new_true = lamb_expr_true(*a4) + if i == p: + interactions_true += s_new_true + ############### + + #slope of line y = mx + mask_on_axis = m*np.abs(coord[0]) >= np.abs(coord[1]) + mask_off_axis = m*np.abs(coord[0]) < np.abs(coord[1]) + + interactions_off_axis = interactions_off_axis.reshape(coord[0].shape) + + interactions_total = interactions_on_axis * 0 + interactions_total[mask_on_axis] = interactions_on_axis[mask_on_axis] + interactions_total[mask_off_axis] = interactions_off_axis[mask_off_axis] + + return interactions_on_axis, interactions_off_axis, interactions_true, interactions_total + +def create_logarithmic_mesh(res): + + x_grid = [10**(pw) for pw in np.linspace(-8, 0, res)] + y_grid = [10**(pw) for pw in np.linspace(-8, 0, res)] + + mesh = np.meshgrid(x_grid, y_grid) + mesh_points = np.array(mesh).reshape(2, -1) + + return mesh_points, x_grid, y_grid + +def create_plot(relerr_on, ax, str_title, acbar=True): + n_levels = 18 + levels = 10**np.linspace(-n_levels+2, 1, n_levels) + cs = ax.contourf(x_grid, y_grid, relerr_on.reshape(res, res), locator=ticker.LogLocator(), cmap=cm.coolwarm, levels=levels, extend="both") + if acbar: + cbar = fig.colorbar(cs) + cbar.set_ticks(levels) + cbar.set_ticklabels(["1e"+str(int(i)) for i in np.linspace(-n_levels+2, 1, n_levels)]) + + ax.set_xscale('log') + ax.set_yscale('log') + ax.set_xlabel("$x_1$-coordinate", fontsize=15) + ax.set_ylabel("$x_2$-coordinate", fontsize=15) + ax.set_title(str_title) + + return cs + +def create_suite_plot(relerr_on, relerr_off, relerr_comb, str_title): + fig, (ax1,ax2,ax3) = plt.subplots(1, 3, figsize=(15, 8)) + cs = create_plot(relerr_on, ax1, "Large-$|x_1|$ Recurrence", False) + cs = create_plot(relerr_off, ax2, "Small-$|x_1|$ Recurrence ($p_{offaxis}=8$)", False) + cs = create_plot(relerr_comb, ax3, "Large/Small-$|x_1|$ Recurrence ($\\xi=50$)", False) + + n_levels = 18 + levels = 10**np.linspace(-n_levels+2, 1, n_levels) + + + fig.subplots_adjust(wspace=0.3, hspace=0.5) + + cbar = fig.colorbar(cs, ax=[ax1,ax2,ax3], shrink=0.9, location='bottom') + cbar.set_ticks(levels) + cbar.set_ticklabels(["1e"+str(int(i)) for i in np.linspace(-n_levels+2, 1, n_levels)]) + fig.suptitle(str_title, fontsize=16) + +#========================= DEFINE PLOT RESOLUTION ==================================== +res = 32 +mesh_points, x_grid, y_grid = create_logarithmic_mesh(res) + +#========================= DEFINE GREEN'S FUNCTIONS/PDE's ==================================== +from collections import namedtuple +DerivativeIdentifier = namedtuple("DerivativeIdentifier", ["mi", "vec_idx"]) +var = _make_sympy_vec("x", 2) +var_t = _make_sympy_vec("t", 2) +abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2) +w = make_identity_diff_op(2) + +partial_4x = DerivativeIdentifier((4,0), 0) +partial_4y = DerivativeIdentifier((0,4), 0) +partial_2x2y = DerivativeIdentifier((2,2), 0) +biharmonic_op = {partial_4x: 1, partial_4y: 1, partial_2x2y:2} +list_pde = immutabledict(biharmonic_op) + +biharmonic_pde = LinearPDESystemOperator(2, (list_pde,)) +g_x_y_biharmonic = abs_dist**2 * sp.log(abs_dist) + +laplace2d = laplacian(w) +g_x_y_laplace = (-1/(2*np.pi)) * sp.log(abs_dist) + +k = 1 +helmholtz2d = laplacian(w) + w +g_x_y_helmholtz = (1j/4) * hankel1(0, k * abs_dist) +#========================= LAPLACE 2D ==================================== +interactions_on_axis, interactions_off_axis, interactions_true, interactions_total = produce_error_for_recurrences(mesh_points, laplace2d, g_x_y_laplace, 10,m=1e2/2) + +relerr_on = np.abs((interactions_on_axis-interactions_true)/interactions_true) +relerr_off = np.abs((interactions_off_axis-interactions_true)/interactions_true) +relerr_comb = np.abs((interactions_total-interactions_true)/interactions_true) + +create_suite_plot(relerr_on+1e-20, relerr_off+1e-20, relerr_comb+1e-20, "Laplace 2D: 9th Order Derivative Evaluation Error $(u_{recur}-u_{sympy})/u_{recur}$") + +#========================= HELMOLTZ 2D ==================================== +#interactions_on_axis, interactions_off_axis, interactions_true, interactions_total = produce_error_for_recurrences(mesh_points, helmholtz2d, g_x_y_helmholtz, 8) + +#relerr_on = np.abs((interactions_on_axis-interactions_true)/interactions_true) +#relerr_off = np.abs((interactions_off_axis-interactions_true)/interactions_true) +#relerr_comb = np.abs((interactions_total-interactions_true)/interactions_true) + +#create_suite_plot(relerr_on, relerr_off, relerr_comb, "Helmholtz 2D: 8th Order Derivative Evaluation Error $(u_{recur}-u_{sympy})/u_{recur}$") + + +#======================== BIHARMONIC 2D =================================== +#interactions_on_axis, interactions_off_axis, interactions_true, interactions_total = produce_error_for_recurrences(mesh_points, biharmonic_pde, g_x_y_biharmonic, 8, m=1e2/2) + +#relerr_on = np.abs((interactions_on_axis-interactions_true)/interactions_true) +#relerr_off = np.abs((interactions_off_axis-interactions_true)/interactions_true) +#relerr_comb = np.abs((interactions_total-interactions_true)/interactions_true) + +#create_suite_plot(relerr_on+1e-20, relerr_off+1e-20, relerr_comb+1e-20, "Biharmonic 2D: 8th Order Derivative Evaluation Error $(u_{recur}-u_{sympy})/u_{recur}$") + +plt.savefig("../S_on_surface_convergence.pgf", bbox_inches='tight', pad_inches=0) +plt.show() \ No newline at end of file diff --git a/test/test_eigenvalues.ipynb b/test/test_eigenvalues.ipynb new file mode 100644 index 000000000..64d3756a3 --- /dev/null +++ b/test/test_eigenvalues.ipynb @@ -0,0 +1,242 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import sympy as sp\n", + "\n", + "from sumpy.expansion.diff_op import (\n", + " make_identity_diff_op,\n", + ")\n", + "from collections import namedtuple\n", + "DerivativeIdentifier = namedtuple(\"DerivativeIdentifier\", [\"mi\", \"vec_idx\"])\n", + "\n", + "from sumpy.recurrence import _make_sympy_vec, get_reindexed_and_center_origin_on_axis_recurrence\n", + "\n", + "from immutabledict import immutabledict\n", + "from sumpy.expansion.diff_op import LinearPDESystemOperator" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from test_recurrence_qbx import _create_ellipse\n", + "n_p = 1000\n", + "a = 2\n", + "mode_nr = 10\n", + "sources, centers, normals, density, jacobs, radius = _create_ellipse(n_p, a=a, quad_convg_rate=100, mode_nr=mode_nr)\n", + "t = np.linspace(0, 2 * np.pi, n_p, endpoint=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def give_true_sol(n_p, a=2, n=10):\n", + " r = 1/a\n", + " mu_n = 1/(2*n) * (1 + ((1-r)/(1+r))**n)\n", + "\n", + " phi = sp.symbols(\"phi\")\n", + " jacob = sp.sqrt(a**2 * sp.sin(phi)**2 + sp.cos(phi)**2)\n", + "\n", + " t = np.linspace(0, 2 * np.pi, n_p, endpoint=False)\n", + " true_sol = mu_n * sp.lambdify(phi, jacob)(t) * density\n", + "\n", + " return true_sol" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from sumpy.array_context import _acf\n", + "from sumpy.expansion.local import LineTaylorLocalExpansion\n", + "from sumpy.kernel import LaplaceKernel\n", + "from test_recurrence_qbx import _qbx_lp_general\n", + "actx_factory = _acf\n", + "ExpnClass = LineTaylorLocalExpansion\n", + "\n", + "actx = actx_factory()\n", + "lknl2d = LaplaceKernel(2)\n", + "strengths = jacobs * density * (2*np.pi/(n_p)) \n", + "p = 11\n", + "qbx_res = _qbx_lp_general(lknl2d, sources, sources, centers,\n", + " radius, strengths, p)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "true_sol = give_true_sol(n_p, a=a, n=mode_nr)\n", + "rel_err = np.max(np.abs(qbx_res-true_sol))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "h = 9.69/n_p" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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O9jtdsydOxdLly7FM888ozmt0XbXUh+1HDp0iKoNW79+vUtLevbsWaxYsYLqk7hy5cquja+HDx92aVzT09Px9ddfo379+jxta5cuXeDv748OHTpgzpw5stqXiIgIvPrqq7ruS8v9ATku8saOHYsLFy64Nou88sor2Lp1K5YuXYpff/0VdrsdkydPxrBhw1zXHTlyBHXr1gUAjBw5Etu3b8fmzZsRHByMXr16Yc6cORgyZAiAnElQZmYmUlJSNLmMdFKyZElYrVZcv35ddM7pHkhppadkyZIuzbGzXkLU2KAKNThm5auEnnL0ahOVKF26NKZMmYIpU6bg0qVL+Pvvv/HJJ5/g1q1bWLNmje58nZumjb77NO7cuYObN29i0qRJIpeZQE779urVCytWrNBUp7p162LRokUibdvRo0cB8F1q1atXj+pn1rkC5tw0qiVPLXUFcgTQ0aNHY/To0fjf//4nmU4tWuuqVL7a5zRlyhTVz1PvsxfCXalU+z0GBgZi//79vOM1atSQLYeG2neHy7179+Dt7a1aAHZmqAm17qe0uEjr0aOHyAVKdna2y4XUzp07qdfrdZGmtTw9Lruys7NJZGQkqVKlCtm8ebPo3/vvv08AkH/++UdVOZ06dSIAyLlz5wghOfZHNHbv3k0AkFdeecXQfau55zNnzhCLxUI++ugj0blVq1YRAGTBggXUawu6i7R79+6RkiVLklq1arlcXGlJS4i57S5VhloyMjIIADJ69GjecacdrfMZzp49m9SvX5+aR58+fVx2kVWqVCF+fn4kODiY2Gw2UqFCBde76SQ9PZ1ER0eT6tWra66vVtTen5N+/fq57PQJISQmJoZ8//33vDSlSpXi3VO/fv3IjBkzyLVr10hAQAC5evWq69ycOXPISy+95Prt3Atw+PBhUV2lbOmFtqjNmjUj4eHhJDU11ZXGbreTunXr8myCp06dSgCQhIQEUVkdO3Yk9evX12WHLWfzryVfIzbBasuRq6vS3gW99X3yySdJaGioqjxppKenk5CQENKkSRPdecjZVaalpVHHntjYWOLj40M2b95Mjh49qrlOzr598eLFvONdunQRuTNbu3YtAUDGjRvHSzt58mQCgGzfvl1znlrqOnbsWNWu55wo2apqqaua8tU+Jy3PU8+zF3Lv3j1StmxZ0qBBA9cxI/2JEKV2VvvucOnUqRNp2LChpnroN3pTyaZNm6iRd7p164YWLVpgypQpGD58OFq2bIm3334bFSpUcAUT2Lt3L6ZMmYLmzZubUpe8LG/16tW4du0avv76a+oyQJ06dTB9+nTMmzcP3bt35507duyYa1n57t27WLZsGdavX4/evXu7NB+xsbEoV64cevTogZo1a8LhcCA+Ph6TJk1CQEAAT4Nl5L7lnl+VKlXw9ttv45tvvsGDBw/QrVs3+Pr6upyIx8TEiByna4HbDlyqVKmiyguJketLliyJESNG4KOPPsLvv/+O/v37a05r5vsmVx8PDw+0adMGGzdulLzey8sLjz32GP766y/0798fvr6+WLFiBSZOnAgg11729u3b1GXj9PR0rF+/HvPnz0dycjLOnTuHU6dOoWrVqsjIyMDgwYMxbNgw/P33365rXn/9dZQtWxb79u3D9u3b0apVK8X71Iva+3Ny+PBh17Kkw+FAQkICT4t89epVZGZmukwogBxN8BtvvIENGzYgPT2dZw5it9t52ijnN79nzx7XrnGtjB8/Hp06dUK7du3wwQcfwMvLCzNnzsSxY8ewaNEil2bTqZ3+/vvvMXDgQHh6eqJGjRoIDAzE999/j5YtW6JVq1Z48803UalSJSQlJeHMmTP4559/RMFq1GIk361bt6JDhw4YOXIkRo4c6bZyzOLhw4do164dXnjhBdSsWdOl6VqzZg2eeuopVzo13yGXFStW4N69e7JaTKm2Wr16NVJSUpCUlAQgx8THudrYrVs3+Pn5wcfHhzr2LFy4EDabjXpOTZ26du2KTp064c0330RiYiKqVq2KRYsWYc2aNfj11195q1qdO3dGjx49MHbsWDgcDjRt2hQHDhzAmDFj0L17d7Rs2VJznmrrOmnSJIwcORJdunTBE088IdIqCvddqGlTLXVVW76W56Q2ndZn/8ILL6BChQqIiYlB6dKlcfr0aUyaNAk3b97EwoULXenM+B7VtrPad8eJw+HAvn378MorryjWgYcmkZlo1wRL/ePOsnfv3k2eeeYZEhYWRjw8PEiZMmXIU089RXbt2iVbhlZNsN7y9Ggmn3zySeLl5UVu3bolmeb5558nHh4eIo8a3H/BwcGkQYMGZPLkySQ9Pd117R9//EFeeOEFUq1aNRIQEEA8PT1JhQoVyIABA6gaIa33rfb5ORwOMmvWLBITE0P8/PyI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Xr3QvHlz7N27F0eOHMHzzz+PwYMH44MPPhCl37BhA65fv47Tp09jzJgxGDduHObPn+86P3PmTFy8eBETJkxwHfviiy9w48YNTJs2TXP9pCCEIDs727T8uNjtdjgcDlVpU1JS0KJFC979CklNTUWXLl3wv//9z6wqFgmysrLclndB6lsIIXj55ZfRp08f6vnExER06tQJkZGR2L9/P6ZNm4Zvv/0WkydPdqU5f/48unXrhlatWuHQoUP43//+h6FDh+LPP/90pdm9ezf69OmDAQMG4PDhwxgwYACee+457N271y339ddff7kmd0IyMzNV5/PCCy8gPj4ea9aswZo1axAfH48BAwbIXjNx4kRMnjwZ06dPx/79+xEeHo5OnTohKSnJlWb37t3o0qULOnfujH379mH//v14++23YbXmDvGZmZl49tln8eabb0qWNXnyZHz66af45JNPcPz4cWzcuBGxsbGu8+fOnUOvXr3Qvn17xMfHY+3atbhz5w6eeuopV5odO3bgxRdfxCuvvILjx49jyZIl2L9/P1599VVReRcvXsQHH3yAVq1aUeujpt/v0qULL82qVatc51JSUtC5c2dYLBZs2rQJO3fuRGZmJnr06EHt9z766CNERkaKjsfFxSE0NBS//vorjh8/jk8//RQjRozA9OnTRWmzsrLQt29f6j1lZGQgNDQUn376KerXr0+9599++w2ffPIJRo0ahRMnTmDevHn4448/MGLECFea+/fvo0WLFvD09MTq1auRkJCASZMmuYTu1NRUHDx4EJ9//jkOHjyIZcuW4dSpU+jZs6eovNdee43Xfj/88AO1XkUWwlCFw+EgX3/9NalcuTLx8fEh9erVI0uWLHGd37x5MwFA7t+/TwghZMGCBSQ4ONh1ftSoUaR+/fqu3wMHDiS9evUio0ePJqGhoSQwMJC8/vrrJCMjw5VmyZIlpE6dOsTHx4eEhISQDh06kOTkZNf5+fPnk5o1axJvb29So0YNMmPGDEP3QAghx44dI926dSOBgYEkICCAtGzZkpw5c8Z1ft68eSQqKop4eXmR8PBwMmTIENVt+ODBA+Lp6UkWL17sOnb16lVitVrJmjVrJK8Ttp0eLl26RDw9Pcl7770nOjd16lQCgOzZs4cQQsj58+cJAHLo0CFeuvbt25O33nqLd+yvv/4iXl5e5PDhw+TAgQPE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pmuuuUbp6elml2MZu3bt0pVXXqno6GgtWLBA69atU1pamtllWcaaNWv0ySef8I2TF5MmTdLq1atVXV2tlStX6vDhw8rPz9exY8fMLs0SvvzyS1VWVmr06NGqrq7WggULtGjRIq1evdrs0mBhEWYXAGtyuVwef7vd7k63AV25//779Y9//EMffvih2aVYypgxY1RfX68TJ07oT3/6k0pKSrRlyxaCsKT9+/frwQcf1KZNmxQTE2N2OZZTWFjY8XtGRoby8vI0cuRIvfbaayorKzOxMmtob29XTk6OnnnmGUnShAkT9Nlnn6myslJ33XWXydXBqugJhodBgwYpPDy8U69vU1NTp95hwJsHHnhAb7/9tjZv3qzhw4ebXY6lREVFadSoUcrJyVF5ebmysrL0+9//3uyyLKGurk5NTU3Kzs5WRESEIiIitGXLFv3hD39QRESE2trazC7RUvr27auMjAx98cUXZpdiCUOHDu30YXLs2LGc0A2fCMHwEBUVpezsbNXU1HjcXlNTo/z8fJOqgh243W7df//9+vOf/6z3339fqampZpdkeW63W62trWaXYQnTp0/Xrl27VF9f3/GTk5OjO++8U/X19QoPDze7REtpbW3V7t27NXToULNLsYTJkyd3mpLx888/V0pKikkVwQ4YDoFOysrKNHfuXOXk5CgvL09VVVVqbGzUggULzC7NdKdOndLevXs7/t63b5/q6+sVHx+v5ORkEyszX2lpqd544w299dZbio2N7fg2IS4uTldccYXJ1ZnvN7/5jQoLC5WUlKRvv/1Wa9as0QcffKB3333X7NIsITY2ttP48b59+2rgwIGMK5f08MMPq6ioSMnJyWpqatJTTz2l5uZmlZSUmF2aJTz00EPKz8/XM888o5/85Cf6+OOPVVVVpaqqKrNLg5W5AS8qKircKSkp7qioKPfEiRPdW7ZsMbskS9i8ebNbUqefkpISs0sznbd2keR+9dVXzS7NEu69996ObWrw4MHu6dOnuzdt2mR2WZZ27bXXuh988EGzy7CEOXPmuIcOHeqOjIx0JyYmumfPnu3+7LPPzC7LUv7yl7+409PT3dHR0e6rrrrKXVVVZXZJsDjmCQYAAEDIYUwwAAAAQg4hGAAAACGHEAwAAICQQwgGAABAyCEEAwAAIOQQggEAABByCMEAAAAIOYRgAAAAhBxCMAAAAEIOIRgAAAAhhxAMAACAkEMIBgAAQMj5/5QbQ+Fbl8QdAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "fig, ax = plt.subplots(1,1)\n", + "ax.set_yscale('log')\n", + "ax.plot(t, (abs(qbx_res-true_sol)+1e-20), label=\"absolute error\")\n", + "plt.suptitle(\"PLOT OF ABSOLUTE ERROR: $u_{QBX}-u_{true}$\"+ \"(note rel. err is: \" + str(rel_err)+\")\")\n", + "ax.set_title(\"ellipse ecc: \"+str(1/a)+\", QBX order: \"+str(p) + \", number points: \" + str(n_p) + \", h/r: \"+ str(h/radius), fontdict={'size': 10})\n", + "ax.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.15707963267948966" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "radius" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.009689999999999999" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "h" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.061688455942418625" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "h/radius" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.1" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1/10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "inteq", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/test_recurrence.py b/test/test_recurrence.py new file mode 100644 index 000000000..fa9adbee2 --- /dev/null +++ b/test/test_recurrence.py @@ -0,0 +1,537 @@ +r""" +With the functionality in this module, we aim to test recurrence +code. + +.. autofunction:: test_laplace3d +.. autofunction:: test_helmholtz3d +.. autofunction:: test_laplace2d +.. autofunction:: test_helmholtz2d +.. autofunction:: test_laplace_2d_off_axis +""" +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +import numpy as np +import sympy as sp +from sympy import hankel1 + +from sumpy.expansion.diff_op import ( + laplacian, + make_identity_diff_op, +) +from sumpy.recurrence import _make_sympy_vec, get_reindexed_and_center_origin_on_axis_recurrence, get_off_axis_expression, get_reindexed_and_center_origin_off_axis_recurrence +import math + +from immutabledict import immutabledict +from sumpy.expansion.diff_op import LinearPDESystemOperator + +def test_laplace3d(): + r""" + Tests recurrence code for orders up to 6 laplace3d. + """ + w = make_identity_diff_op(3) + laplace3d = laplacian(w) + n_init, _, r = get_reindexed_and_center_origin_on_axis_recurrence(laplace3d) + n = sp.symbols("n") + s = sp.Function("s") + + var = _make_sympy_vec("x", 3) + var_t = _make_sympy_vec("t", 3) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2 + (var[2]-var_t[2])**2) + g_x_y = 1/abs_dist + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0).subs(var_t[2], 0) + for i in range(6)] + + # pylint: disable-next=not-callable + subs_dict = {s(0): derivs[0], s(1): derivs[1]} + check = [] + + assert n_init == 2 + max_order_check = 6 + for i in range(n_init, max_order_check): + check.append(r.subs(n, i).subs(subs_dict) - derivs[i]) + # pylint: disable-next=not-callable + subs_dict[s(i)] = derivs[i] + + x_coord = np.random.rand() # noqa: NPY002 + y_coord = np.random.rand() # noqa: NPY002 + z_coord = np.random.rand() # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord, var[2]: z_coord} + + check = np.array([check[i].subs(coord_dict) for i in range(len(check))]) + + assert max(abs(check)) <= 1e-12 + + +def test_helmholtz3d(): + r""" + Tests recurrence code for orders up to 6 helmholtz3d. + """ + w = make_identity_diff_op(3) + helmholtz3d = laplacian(w) + w + n_init, _, r = get_reindexed_and_center_origin_on_axis_recurrence(helmholtz3d) + + n = sp.symbols("n") + s = sp.Function("s") + + var = _make_sympy_vec("x", 3) + var_t = _make_sympy_vec("t", 3) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2 + (var[2]-var_t[2])**2) + g_x_y = sp.exp(1j * abs_dist) / abs_dist + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0).subs(var_t[2], 0) + for i in range(6)] + + # pylint: disable-next=not-callable + subs_dict = {s(0): derivs[0], s(1): derivs[1]} + check = [] + + assert n_init == 2 + max_order_check = 6 + for i in range(n_init, max_order_check): + check.append(r.subs(n, i).subs(subs_dict) - derivs[i]) + # pylint: disable-next=not-callable + subs_dict[s(i)] = derivs[i] + + x_coord = np.random.rand() # noqa: NPY002 + y_coord = np.random.rand() # noqa: NPY002 + z_coord = np.random.rand() # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord, var[2]: z_coord} + + check = np.array([check[i].subs(coord_dict) for i in range(len(check))]) + + assert max(abs(abs(check))) <= 1e-12 + +def test_biharmonic2d(): + r""" + Tests recurrence code for orders up to 6 biharmonic. + """ + + from collections import namedtuple + DerivativeIdentifier = namedtuple("DerivativeIdentifier", ["mi", "vec_idx"]) + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2) + partial_4x = DerivativeIdentifier((4,0), 0) + partial_4y = DerivativeIdentifier((0,4), 0) + partial_2x2y = DerivativeIdentifier((2,2), 0) + biharmonic_op = {partial_4x: 1, partial_4y: 1, partial_2x2y:2} + list_pde = immutabledict(biharmonic_op) + biharmonic_pde = LinearPDESystemOperator(2, (list_pde,)) + + g_x_y = abs_dist**2 * (sp.log(abs_dist)) + + n_init, _, r = get_reindexed_and_center_origin_on_axis_recurrence(biharmonic_pde) + + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0) + for i in range(8)] + + x_coord = np.random.rand() # noqa: NPY002 + y_coord = np.random.rand() * 1e-3 # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord} + derivs = [d.subs(coord_dict) for d in derivs] + + n = sp.symbols("n") + s = sp.Function("s") + + # pylint: disable-next=not-callable + subs_dict = {s(0): derivs[0], s(1): derivs[1], s(2): derivs[2], s(3): derivs[3]} + check = [] + + assert n_init == 4 + max_order_check = 8 + for i in range(n_init, max_order_check): + check.append(r.subs(n, i).subs(subs_dict) - derivs[i]) + # pylint: disable-next=not-callable + subs_dict[s(i)] = derivs[i] + + f2 = sp.lambdify([var[0], var[1]], check[0]) + assert abs(f2(x_coord, y_coord)) <= 1e-11 + f3 = sp.lambdify([var[0], var[1]], check[1]) + assert abs(f3(x_coord, y_coord)) <= 1e-11 + f4 = sp.lambdify([var[0], var[1]], check[2]) + assert abs(f4(x_coord, y_coord)) <= 1e-11 + f5 = sp.lambdify([var[0], var[1]], check[3]) + assert abs(f5(x_coord, y_coord)) <= 1e-11 + +test_biharmonic2d() + + + +def test_helmholtz2d(): + r""" + Tests recurrence code for orders up to 6 helmholtz2d. + """ + w = make_identity_diff_op(2) + helmholtz2d = laplacian(w) + w + n_init, _, r = get_reindexed_and_center_origin_on_axis_recurrence(helmholtz2d) + + n = sp.symbols("n") + s = sp.Function("s") + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2) + k = 1 + g_x_y = (1j/4) * hankel1(0, k * abs_dist) + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0) + for i in range(6)] + x_coord = np.random.rand() # noqa: NPY002 + y_coord = np.random.rand() # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord} + derivs = [d.subs(coord_dict) for d in derivs] + + # pylint: disable-next=not-callable + subs_dict = {s(0): derivs[0], s(1): derivs[1]} + check = [] + + assert n_init == 2 + max_order_check = 6 + for i in range(n_init, max_order_check): + check.append(r.subs(n, i).subs(subs_dict) - derivs[i]) + # pylint: disable-next=not-callable + subs_dict[s(i)] = derivs[i] + + f2 = sp.lambdify([var[0], var[1]], check[0]) + assert abs(f2(x_coord, y_coord)) <= 1e-13 + f3 = sp.lambdify([var[0], var[1]], check[1]) + assert abs(f3(x_coord, y_coord)) <= 1e-13 + f4 = sp.lambdify([var[0], var[1]], check[2]) + assert abs(f4(x_coord, y_coord)) <= 1e-13 + f5 = sp.lambdify([var[0], var[1]], check[3]) + assert abs(f5(x_coord, y_coord)) <= 1e-12 + + +def test_laplace2d(): + r""" + Tests recurrence code for orders up to 6 laplace2d. + """ + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + n_init, _, r = get_reindexed_and_center_origin_on_axis_recurrence(laplace2d) + + n = sp.symbols("n") + s = sp.Function("s") + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + g_x_y = sp.log(sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2)) + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0) + for i in range(6)] + + # pylint: disable-next=not-callable + subs_dict = {s(0): derivs[0], s(1): derivs[1]} + check = [] + + assert n_init == 2 + max_order_check = 6 + for i in range(n_init, max_order_check): + check.append(r.subs(n, i).subs(subs_dict) - derivs[i]) + # pylint: disable-next=not-callable + subs_dict[s(i)] = derivs[i] + + x_coord = np.random.rand() # noqa: NPY002 + y_coord = np.random.rand() # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord} + + check = np.array([check[i].subs(coord_dict) for i in range(len(check))]) + assert max(abs(abs(check))) <= 1e-12 + + +def test_helmholtz_2d_off_axis(deriv_order, exp_order): + r""" + Tests off-axis recurrence code for orders up to 6 laplace2d. + """ + w = make_identity_diff_op(2) + helmholtz2d = laplacian(w) + w + + n = sp.symbols("n") + s = sp.Function("s") + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2) + k = 1 + g_x_y = (1j/4) * hankel1(0, k * abs_dist) + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0) + for i in range(6)] + + x_coord = 1e-2 * np.random.rand() # noqa: NPY002 + y_coord = np.random.rand() # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord} + start_order, recur_order, recur = get_reindexed_and_center_origin_off_axis_recurrence(helmholtz2d) + + ic = [] + #Generate ic + + for i in range(start_order): + ic.append(derivs[i].subs(var[0], 0).subs(var[1], coord_dict[var[1]])) + + n = sp.symbols("n") + for i in range(start_order, 15): + recur_eval = recur.subs(var[0], coord_dict[var[0]]).subs(var[1], coord_dict[var[1]]).subs(n, i) + for j in range(i-recur_order, i): + recur_eval = recur_eval.subs(s(j), ic[j]) + ic.append(recur_eval) + + ic = np.array(ic) + + #true_ic = np.array([derivs[i].subs(var[0], 0).subs(var[1], coord_dict[var[1]]) for i in range(15)]) + + #assert np.max(np.abs(ic[::2]-true_ic[::2])/np.abs(true_ic[::2])) < 1e-8 + #print(np.max(np.abs(ic[::2]-true_ic[::2])/np.abs(true_ic[::2]))) + + # CHECK ACCURACY OF EXPRESSION FOR deriv_order + + exp, exp_range, _ = get_off_axis_expression(helmholtz2d, exp_order) + approx_deriv = exp.subs(n, deriv_order) + for i in range(-exp_range+deriv_order, deriv_order+1): + approx_deriv = approx_deriv.subs(s(i), ic[i]) + + rat = coord_dict[var[0]]/coord_dict[var[1]] + if deriv_order + exp_order % 2 == 0: + prederror = abs((ic[deriv_order+exp_order+2] * coord_dict[var[0]]**(exp_order+2)/math.factorial(exp_order+2)).evalf()) + else: + prederror = abs((ic[deriv_order+exp_order+1] * coord_dict[var[0]]**(exp_order+1)/math.factorial(exp_order+1)).evalf()) + print("PREDICTED ERROR: ", prederror) + relerr = abs(((approx_deriv - derivs[deriv_order])/derivs[deriv_order]).subs(var[0], coord_dict[var[0]]).subs(var[1], coord_dict[var[1]]).evalf()) + print("RELATIVE ERROR: ", relerr) + print("RATIO(x0/x1): ", rat) + #assert relerr <= prederror + + +def test_helmholtz_2d_off_axis(deriv_order, exp_order): + r""" + Tests off-axis recurrence code for orders up to 6 laplace2d. + """ + w = make_identity_diff_op(2) + helmholtz2d = laplacian(w) + w + + n = sp.symbols("n") + s = sp.Function("s") + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2) + g_x_y = abs_dist**2*sp.log(abs_dist) + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0) + for i in range(6)] + + x_coord = 1e-2 * np.random.rand() # noqa: NPY002 + y_coord = np.random.rand() # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord} + start_order, recur_order, recur = get_reindexed_and_center_origin_off_axis_recurrence(helmholtz2d) + + ic = [] + #Generate ic + + for i in range(start_order): + ic.append(derivs[i].subs(var[0], 0).subs(var[1], coord_dict[var[1]])) + + n = sp.symbols("n") + for i in range(start_order, 15): + recur_eval = recur.subs(var[0], coord_dict[var[0]]).subs(var[1], coord_dict[var[1]]).subs(n, i) + for j in range(i-recur_order, i): + recur_eval = recur_eval.subs(s(j), ic[j]) + ic.append(recur_eval) + + ic = np.array(ic) + + #true_ic = np.array([derivs[i].subs(var[0], 0).subs(var[1], coord_dict[var[1]]) for i in range(15)]) + + #assert np.max(np.abs(ic[::2]-true_ic[::2])/np.abs(true_ic[::2])) < 1e-8 + #print(np.max(np.abs(ic[::2]-true_ic[::2])/np.abs(true_ic[::2]))) + + # CHECK ACCURACY OF EXPRESSION FOR deriv_order + + exp, exp_range, _ = get_off_axis_expression(helmholtz2d, exp_order) + approx_deriv = exp.subs(n, deriv_order) + for i in range(-exp_range+deriv_order, deriv_order+1): + approx_deriv = approx_deriv.subs(s(i), ic[i]) + + rat = coord_dict[var[0]]/coord_dict[var[1]] + if deriv_order + exp_order % 2 == 0: + prederror = abs((ic[deriv_order+exp_order+2] * coord_dict[var[0]]**(exp_order+2)/math.factorial(exp_order+2)).evalf()) + else: + prederror = abs((ic[deriv_order+exp_order+1] * coord_dict[var[0]]**(exp_order+1)/math.factorial(exp_order+1)).evalf()) + print("PREDICTED ERROR: ", prederror) + relerr = abs(((approx_deriv - derivs[deriv_order])/derivs[deriv_order]).subs(var[0], coord_dict[var[0]]).subs(var[1], coord_dict[var[1]]).evalf()) + print("RELATIVE ERROR: ", relerr) + print("RATIO(x0/x1): ", rat) + #assert relerr <= prederror + + +def test_laplace_2d_off_axis(deriv_order, exp_order): + r""" + Tests off-axis recurrence code for orders up to 6 laplace2d. + """ + max_deriv = deriv_order+exp_order+2 + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + g_x_y = sp.log(sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2)) + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0) + for i in range(max_deriv)] + s = sp.Function("s") + + x_coord = 0.0006490822305146929#1e-2 * np.random.rand() # noqa: NPY002 + y_coord = -0.06766742499535426#np.random.rand() # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord} + + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + start_order, recur_order, recur = get_reindexed_and_center_origin_off_axis_recurrence(laplace2d) + + ic = [] + #Generate ic + + for i in range(start_order): + ic.append(derivs[i].subs(var[0], 0).subs(var[1], coord_dict[var[1]])) + + n = sp.symbols("n") + for i in range(start_order, max_deriv): + recur_eval = recur.subs(var[0], coord_dict[var[0]]).subs(var[1], coord_dict[var[1]]).subs(n, i) + for j in range(i-recur_order, i): + recur_eval = recur_eval.subs(s(j), ic[j]) + ic.append(recur_eval) + + ic = np.array(ic) + + #true_ic = np.array([derivs[i].subs(var[0], 0).subs(var[1], coord_dict[var[1]]) for i in range(max_deriv)]) + + #assert np.max(np.abs(ic[::2]-true_ic[::2])/np.abs(true_ic[::2])) < 1e-8 + #print(np.max(np.abs(ic[::2]-true_ic[::2])/np.abs(true_ic[::2]))) + + # CHECK ACCURACY OF EXPRESSION FOR deriv_order + + exp, exp_range, start_order = get_off_axis_expression(laplace2d, exp_order) + + assert deriv_order >= start_order + approx_deriv = exp.subs(n, deriv_order) + for i in range(-exp_range+deriv_order, deriv_order+1): + approx_deriv = approx_deriv.subs(s(i), ic[i]) + + + rat = coord_dict[var[0]]/coord_dict[var[1]] + if deriv_order + exp_order % 2 == 0: + prederror = abs(ic[deriv_order+exp_order+2] * coord_dict[var[0]]**(exp_order+2)/math.factorial(exp_order+2)) + else: + prederror = abs(ic[deriv_order+exp_order+1] * coord_dict[var[0]]**(exp_order+1)/math.factorial(exp_order+1)) + print("PREDICTED ERROR: ", prederror) + + + relerr = abs((approx_deriv - derivs[deriv_order])/derivs[deriv_order]).subs(var[0], coord_dict[var[0]]).subs(var[1], coord_dict[var[1]]) + print("RELATIVE ERROR: ", relerr) + print("RATIO(x0/x1): ", rat) + + assert relerr <= prederror + return relerr + + +""" +import matplotlib.pyplot as plt + +orders_for_plot = [5, 7, 9] +exp_orders = [4, 5, 6, 7, 8] + +X_P = [] +for i in exp_orders: + TEMP = [] + for j in orders_for_plot: + TEMP.append(test_laplace_2d_off_axis(j, i)) + X_P.append(TEMP) + +fig, ax = plt.subplots() + +for i in range(len(exp_orders)): + ax.plot(orders_for_plot, X_P[i], label="EXP ORDER: " +str(exp_orders[i])) + +ax.set_yscale('log') +ax.set_xlabel('Deriv Order') +ax.set_ylabel('Error') +plt.legend() +plt.show() +""" + + + + + + +def _plot_laplace_2d(max_order_check, max_abs): + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + n_init, _, r = get_reindexed_and_center_origin_on_axis_recurrence(laplace2d) + + n = sp.symbols("n") + s = sp.Function("s") + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + g_x_y = sp.log(sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2)) + derivs = [sp.diff(g_x_y, + var_t[0], i).subs(var_t[0], 0).subs(var_t[1], 0) + for i in range(max_order_check)] + + # pylint: disable-next=not-callable + subs_dict = {s(0): derivs[0], s(1): derivs[1]} + check = [] + + assert n_init == 2 + for i in range(n_init, max_order_check): + check.append(abs(r.subs(n, i).subs(subs_dict) - derivs[i])/abs(derivs[i])) + # pylint: disable-next=not-callable + subs_dict[s(i)] = derivs[i] + + x_coord = abs(np.random.rand()*max_abs) # noqa: NPY002 + y_coord = abs(np.random.rand()*max_abs) # noqa: NPY002 + coord_dict = {var[0]: x_coord, var[1]: y_coord} + + return np.array([check[i].subs(coord_dict) for i in range(len(check))]) + + +""" plot_me = _plot_laplace_2d(13, 1) + +fig = plt.figure() +ax = fig.add_subplot(1, 1, 1) +line, = ax.plot([i+2 for i in range(len(plot_me))], plot_me) +ax.set_yscale('log') +plt.ylabel("Error") +plt.xlabel("Order") +plt.show() """ + diff --git a/test/test_recurrence_qbx.py b/test/test_recurrence_qbx.py new file mode 100644 index 000000000..b4799dffc --- /dev/null +++ b/test/test_recurrence_qbx.py @@ -0,0 +1,345 @@ +r""" +With the functionality in this module, we test recurrence ++ qbx code. +""" +from __future__ import annotations + + +__copyright__ = """ +Copyright (C) 2024 Hirish Chandrasekaran +Copyright (C) 2024 Andreas Kloeckner +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +import meshmode.mesh.generation as mgen # type: ignore +import numpy as np +import sympy as sp +from meshmode import _acf as _acf_meshmode # type: ignore +from meshmode.discretization import Discretization # type: ignore +from meshmode.discretization.poly_element import ( # type: ignore + default_simplex_group_factory, +) +from pytential import bind, sym # type: ignore +from sympy import hankel1 + +from sumpy.array_context import _acf +from sumpy.expansion.diff_op import ( + laplacian, + make_identity_diff_op, +) +from sumpy.expansion.local import LineTaylorLocalExpansion +from sumpy.kernel import HelmholtzKernel, LaplaceKernel +from sumpy.qbx import LayerPotential +from sumpy.recurrence_qbx import ( + _compute_rotated_shifted_coordinates, + _make_sympy_vec, + recurrence_qbx_lp, +) + + +actx_factory = _acf +ExpnClass = LineTaylorLocalExpansion + +actx = actx_factory() +lknl2d = LaplaceKernel(2) +hknl2d = HelmholtzKernel(2) +lknl3d = LaplaceKernel(3) +hknl3d = HelmholtzKernel(3) + + +def _qbx_lp_general(knl, sources, targets, centers, radius, + strengths, order, k=0): + lpot = LayerPotential(actx.context, + expansion=ExpnClass(knl, order), + target_kernels=(knl,), + source_kernels=(knl,)) + + # print(lpot.get_kernel()) + expansion_radii = actx.from_numpy(radius * np.ones(sources.shape[1])) + sources = actx.from_numpy(sources) + targets = actx.from_numpy(targets) + centers = actx.from_numpy(centers) + + strengths = (strengths,) + if k == 0: + _evt, (result_qbx,) = lpot( + actx.queue, + targets, sources, centers, strengths, + expansion_radii=expansion_radii) + else: + _evt, (result_qbx,) = lpot( + actx.queue, + targets, sources, centers, strengths, + expansion_radii=expansion_radii, + k=1) + + result_qbx = actx.to_numpy(result_qbx) + + return result_qbx + + +def _create_ellipse(n_p, mode_nr = 10, quad_convg_rate=100, a=2): + t = np.linspace(0, 2 * np.pi, n_p, endpoint=False) + + phi = sp.symbols("phi") + jacob = sp.sqrt(a**2 * sp.sin(phi)**2 + sp.cos(phi)**2) + + jacobs = sp.lambdify(phi, jacob)(t) + + h = ((2*np.pi)/n_p * np.min(jacobs)) + radius = (h/4) * quad_convg_rate + + unit_circle_param = np.exp(1j * t) + unit_circle = np.array([a * unit_circle_param.real, unit_circle_param.imag]) + + sources = unit_circle + normals = np.array([unit_circle_param.real, a*unit_circle_param.imag]) + normals = normals / np.linalg.norm(normals, axis=0) + centers = sources - normals * radius + + density = np.cos(mode_nr * t) * sp.lambdify(phi, 1/jacob)(t) + + return sources, centers, normals, density, jacobs, radius + + +def _create_sphere(refinment_rounds, exp_radius): + target_order = 4 + + actx_m = _acf_meshmode() + mesh = mgen.generate_sphere(1.0, target_order, + uniform_refinement_rounds=refinment_rounds) + grp_factory = default_simplex_group_factory(3, target_order) + discr = Discretization(actx_m, mesh, grp_factory) + nodes = actx_m.to_numpy(discr.nodes()) + sources = np.array([nodes[0][0].reshape(-1), + nodes[1][0].reshape(-1), nodes[2][0].reshape(-1)]) + + area_weight_a = bind(discr, sym.QWeight()*sym.area_element(3))(actx_m) + area_weight = actx_m.to_numpy(area_weight_a)[0] + area_weight = area_weight.reshape(-1) + + normals_a = bind(discr, sym.normal(3))(actx_m).as_vector(dtype=object) + normals_a = actx_m.to_numpy(normals_a) + normals = np.array([normals_a[0][0].reshape(-1), normals_a[1][0].reshape(-1), + normals_a[2][0].reshape(-1)]) + + radius = exp_radius + centers = sources - radius * normals + + return sources, centers, normals, area_weight, radius + + +def test_compute_rotated_shifted_coordinates(): + r""" + Tests rotated shifted code. + """ + sources = np.array([[1], [2], [2]]) + centers = np.array([[0], [0], [0]]) + normals = np.array([[1], [0], [0]]) + cts = _compute_rotated_shifted_coordinates(sources, centers, normals) + assert np.sqrt(cts[1]**2 + cts[2]**2) - np.sqrt(8) <= 1e-12 + + +def test_recurrence_laplace_3d_sphere(): + r""" + Tests reccurrence + qbx laplace 3d on sphere + """ + radius = 0.0001 + sources, centers, normals, area_weight, radius = _create_sphere(1, radius) + + out = _qbx_lp_general(lknl3d, sources, sources, centers, radius, + area_weight, 4) + + w = make_identity_diff_op(3) + laplace3d = laplacian(w) + var = _make_sympy_vec("x", 3) + var_t = _make_sympy_vec("t", 3) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2 + + (var[2]-var_t[2])**2) + g_x_y = 1/(4*np.pi) * 1/abs_dist + + exp_res = recurrence_qbx_lp(sources, centers, normals, area_weight, + radius, laplace3d, g_x_y, 3, 4) + + assert (np.max(exp_res-out)/np.max(abs(exp_res))) <= 1e-12 + + +def test_recurrence_helmholtz_3d_sphere(): + r""" + Tests reccurrence + qbx helmholtz 3d on sphere + """ + # import time + radius = 0.0001 + sources, centers, normals, area_weight, radius = _create_sphere(2, radius) + + # start = time.time() + out = _qbx_lp_general(hknl3d, sources, sources, centers, radius, + np.ones(area_weight.shape), 1, 1) + # end = time.time() + # length1 = end - start + + w = make_identity_diff_op(3) + helmholtz3d = laplacian(w) + w + var = _make_sympy_vec("x", 3) + var_t = _make_sympy_vec("t", 3) + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2 + + (var[2]-var_t[2])**2) + g_x_y = (1/(4*np.pi)) * sp.exp(1j * abs_dist) / abs_dist + + # start = time.time() + exp_res = recurrence_qbx_lp(sources, centers, normals, np.ones(area_weight.shape), + radius, helmholtz3d, g_x_y, 3, 1) + # end = time.time() + # length2 = end - start + # print(sources.shape[1], length1, length2) + + assert np.max(abs(out - exp_res)) <= 1e-8 + + +def test_recurrence_laplace_2d_ellipse(): + r""" + Tests recurrence + qbx code. + """ + + # ------------- 1. Define PDE, Green's Function + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + g_x_y = (-1/(2*np.pi)) * sp.log(sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2)) + + p = 4 + err = [] + for n_p in range(200, 1001, 200): + sources, centers, normals, density, jacobs, radius = _create_ellipse(n_p) + strengths = jacobs * density * (2*np.pi/n_p) + exp_res = recurrence_qbx_lp(sources, centers, normals, + strengths, radius, laplace2d, + g_x_y, 2, p) + qbx_res = _qbx_lp_general(lknl2d, sources, sources, centers, + radius, strengths, p) + # qbx_res,_ = lpot_eval_circle(sources.shape[1], p) + err.append(np.max(np.abs(exp_res - qbx_res))/np.max(np.abs(qbx_res))) + assert np.max(err) <= 1e-13 + + +def test_recurrence_helmholtz_2d_ellipse(): + r""" + Tests recurrence + qbx code. + """ + # ------------- 1. Define PDE, Green's Function + w = make_identity_diff_op(2) + helmholtz2d = laplacian(w) + w + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + k = 1 + abs_dist = sp.sqrt((var[0]-var_t[0])**2 + (var[1]-var_t[1])**2) + g_x_y = (1j/4) * hankel1(0, k * abs_dist) + + p = 5 + err = [] + for n_p in range(200, 1001, 200): + sources, centers, normals, density, jacobs, radius = _create_ellipse(n_p) + strengths = jacobs * density * (2*np.pi/n_p) + exp_res = recurrence_qbx_lp(sources, centers, normals, strengths, + radius, helmholtz2d, g_x_y, 2, p) + qbx_res = _qbx_lp_general(hknl2d, sources, sources, + centers, radius, strengths, p, 1) + err.append(np.max(np.abs(exp_res - qbx_res))) + assert np.max(err) <= 1e-13 + + +def _laplace_2d_true_solution(n_p, density, a=2, n=10): + r = 1/a + mu_n = 1/(2*n) * (1 + ((1-r)/(1+r))**n) + + phi = sp.symbols("phi") + jacob = sp.sqrt(a**2 * sp.sin(phi)**2 + sp.cos(phi)**2) + + t = np.linspace(0, 2 * np.pi, n_p, endpoint=False) + true_sol = mu_n * sp.lambdify(phi, jacob)(t) * density + + return true_sol + + +# ============ Plotting Functionality +def _construct_laplace_axis_2d(orders, resolutions): + w = make_identity_diff_op(2) + laplace2d = laplacian(w) + + var = _make_sympy_vec("x", 2) + var_t = _make_sympy_vec("t", 2) + g_x_y = (-1/(2*np.pi)) * sp.log(sp.sqrt((var[0]-var_t[0])**2 + + (var[1]-var_t[1])**2)) + + err = [] + err1 = [] + for p in orders: + err_per_order = [] + err_per_order1 = [] + for n_p in resolutions: + print("Order:", p, " res:", n_p) + sources, centers, normals, density, jacobs, radius = _create_ellipse(n_p) + strengths = jacobs * density * (2*np.pi/n_p) + exp_res = recurrence_qbx_lp(sources, centers, normals, + strengths, radius, laplace2d, + g_x_y, 2, p) + qbx_res = _qbx_lp_general(lknl2d, sources, sources, centers, + radius, strengths, p) + true_sol = _laplace_2d_true_solution(n_p, density) + # qbx_res,_ = lpot_eval_circle(sources.shape[1], p) + err_per_order.append(np.max(np.abs(exp_res - true_sol)/ + np.max(np.abs(true_sol)))) + err_per_order1.append(np.max(np.abs(true_sol - qbx_res)/ + np.max(np.abs(true_sol)))) + err.append(err_per_order) + err1.append(err_per_order1) + + return err, err1 + + +def plot(): + import matplotlib.pyplot as plt + orders = [5, 7, 9, 11] + colors = ['b', 'g', 'r', 'c'] + resolutions = [2000, 300, 4000] + err_mat, err_mat1 = _construct_laplace_axis_2d(orders, resolutions) + + fig, ax1 = plt.subplots(1, 1, sharey=True, figsize=(6, 6)) + + ax1.set_yscale("log") + for i in range(len(orders)): + ax1.scatter(9.68845/np.array(resolutions), np.array(err_mat[i]), marker='+', label="$u = u_{qbxrec}$ ("+"$p_{QBX}$="+str(orders[i])+ ")", c=colors[i], s=50) + ax1.scatter(9.68845/np.array(resolutions), np.array(err_mat1[i]), marker='x', label="$u = u_{qbx}$ ("+"$p_{QBX}$="+str(orders[i]) + ")", c=colors[i], s=50) + + ax1.set_xlabel("Mesh Resolution ($h$)", fontsize=14) + ax1.set_ylabel("Relative Error ($L_{\infty}$)", fontsize=14) + ax1.set_title("$(u-u_{true})/u_{true}$", fontsize=16) + ax1.legend() + + plt.suptitle("Laplace 2D: Ellipse SLP Boundary Evaluation Error ($m=100$, $p_{offaxis}=8$)", fontsize=16) + plt.savefig("../../S_on_surface_convergence.pgf", bbox_inches='tight', pad_inches=0) + plt.show() + +plot() \ No newline at end of file