\chapter{Hashtables}
\label{hashtablechapter}

The \defrsixlibrary{hashtables} library provides a set of operations on
hashtables.
A \defining{hashtable} is a data structure that associates keys with values.
Any object can be used as a key, provided a \defining{hash function}
and a suitable \defining{equivalence function} is available.  A hash function is a
procedure that maps
keys to exact integer objects.
It is the programmer's responsibility to ensure that the hash function
is compatible with the equivalence function,
which is a procedure that accepts two keys and returns true if they
are equivalent and \schfalse{} otherwise.
Standard hashtables for arbitrary objects based on the {\cf eq?} and
{\cf eqv?} predicates (see report section~\extref{report:equivalencesection}{Equivalence predicates}) are provided.
Also, hash functions for arbitrary objects, strings, and symbols are provided.

This section uses the \var{hashtable} parameter name for arguments
that must be hashtables, and the \var{key} parameter name for
arguments that must be hashtable keys.

\section{Constructors}

\mainindex{hashtable}

\begin{entry}{%
\proto{make-eq-hashtable}{}{procedure}
\rproto{make-eq-hashtable}{ \var{k}}{procedure}}

Returns a newly allocated mutable hashtable that accepts
arbitrary objects as keys,
and compares those keys with {\cf eq?}. If an argument is given, the initial
capacity of the hashtable is set to approximately \var{k} elements.

\end{entry}

\begin{entry}{%
\proto{make-eqv-hashtable}{}{procedure}
\rproto{make-eqv-hashtable}{ \var{k}}{procedure}}

Returns a newly allocated mutable hashtable that accepts
arbitrary objects as keys,
and compares those keys with {\cf eqv?}.
If an argument is given, the initial
capacity of the hashtable is set to approximately \var{k} elements.

\end{entry}

\begin{entry}{%
\proto{make-hashtable}{ \var{hash-function} \var{equiv}}{procedure}
\rproto{make-hashtable}{ \var{hash-function} \var{equiv} \var{k}}{procedure}}

\domain{\var{Hash-function} and \var{equiv} must be procedures.
\var{Hash-function} should accept a key as an argument and should return a
non-negative exact integer object.
\var{Equiv} should accept two keys as arguments and return a single
value.
Neither procedure should mutate the hashtable returned by {\cf make-hashtable}.}
The {\cf make-hashtable} procedure returns a newly allocated mutable
hashtable using \var{hash-function}
as the hash function and \var{equiv} as the equivalence function used to
compare keys.
If a third argument is given, the
initial capacity of the hashtable is set to approximately \var{k} elements.

Both \var{hash-function} and \var{equiv} should behave like pure functions
on the domain of keys.  For example, the {\cf string-hash}
and {\cf string=?} procedures are permissible only if all
keys are strings and the contents of those strings are never
changed so long as any of them continues to serve as a key in
the hashtable.  Furthermore, any pair of keys for which
\var{equiv} returns true should
be hashed to the same exact integer objects by
\var{hash-function}.

\implresp The implementation must check the restrictions on
\var{hash-function} and \var{equiv} to the extent performed by
applying them as described.

\begin{note}
Hashtables are allowed to cache the results of calling the
hash function and equivalence function, so programs cannot
rely on the hash function being called for every lookup or
update.  Furthermore any hashtable operation may call the
hash function more than once.
\end{note}

\end{entry}

\section{Procedures}

\begin{entry}{%
\proto{hashtable?}{ \var{obj}}{procedure}}

Returns \schtrue{} if \var{obj} is a hashtable,
\schfalse{} otherwise.
\end{entry}

\begin{entry}{\proto{hashtable-size}{ \var{hashtable}}{procedure}}

Returns the number of keys contained in \var{hashtable} as an exact
integer object.
\end{entry}

\begin{entry}{%
\proto{hashtable-ref}{ \var{hashtable} \var{key} \var{default}}{procedure}}

Returns the value in \var{hashtable} associated with \var{key}.
If \var{hashtable} does not contain an association for \var{key},
\var{default} is returned.
\end{entry}

\begin{entry}{\proto{hashtable-set!}{ \var{hashtable} \var{key} \var{obj}}{procedure}}

Changes \var{hashtable} to associate \var{key} with \var{obj},
adding a new association or replacing any existing association for \var{key},
and returns \unspecifiedreturn.
\end{entry}

\begin{entry}{\proto{hashtable-delete!}{ \var{hashtable} \var{key}}{procedure}}

Removes any association for \var{key} within \var{hashtable} and
returns \unspecifiedreturn.
\end{entry}

\begin{entry}{\proto{hashtable-contains?}{ \var{hashtable} \var{key}}{procedure}}

Returns \schtrue{} if \var{hashtable} contains an association
for \var{key}, \schfalse{} otherwise.
\end{entry}

\begin{entry}{%
\proto{hashtable-update!}{ \var{hashtable} \var{key} \var{proc} \var{default}}{procedure}}

\domain{\var{Proc} should accept one argument,
should return a single value, and should not mutate \var{hashtable}.}
The {\cf hashtable-update!} procedure applies \var{proc} to the value in \var{hashtable}
associated with \var{key},
or to \var{default} if \var{hashtable} does not contain an
association for \var{key}.
The \var{hashtable} is then changed to associate \var{key}
with the value returned by \var{proc}.

The behavior of {\cf hashtable-update!} is equivalent to the
following code, but may be implemented
more efficiently in cases where the implementation can
avoid multiple lookups of the same key:
\begin{scheme}
(hashtable-set!
  hashtable key
  (proc (hashtable-ref
         hashtable key default)))
\end{scheme}
\end{entry}

\begin{entry}{%
\proto{hashtable-copy}{ \var{hashtable}}{procedure}
\rproto{hashtable-copy}{ \var{hashtable} \var{mutable}}{procedure}}

Returns a copy of \var{hashtable}.  If the
\var{mutable} argument is provided and is true, the returned hashtable is mutable;
otherwise it is immutable.

\end{entry}
\begin{entry}{%
\proto{hashtable-clear!}{ \var{hashtable}}{procedure}
\rproto{hashtable-clear!}{ \var{hashtable} \var{k}}{procedure}}

Removes all associations from \var{hashtable} and returns \unspecifiedreturn.

If a second argument is given, the current
capacity of the hashtable is reset to approximately \var{k} elements.
\end{entry}

\begin{entry}{\proto{hashtable-keys}{ \var{hashtable}}{procedure}}

Returns a vector of all keys in \var{hashtable}.
The order of the vector is unspecified.
\end{entry}

\begin{entry}{%
\proto{hashtable-entries}{ \var{hashtable}}{procedure}}

Returns two values, a vector of the keys in \var{hashtable}, and a
vector of the corresponding values.

\begin{scheme}
(let ((h (make-eqv-hashtable)))
  (hashtable-set! h 1 'one)
  (hashtable-set! h 2 'two)
  (hashtable-set! h 3 'three)
  (hashtable-entries h)) \lev \sharpsign(1 2 3) \sharpsign(one two three)\\\>; \textrm{two return values}
\>; \textrm{entries may be in different order}%
\end{scheme}
\end{entry}

\section{Inspection}

\begin{entry}{\proto{hashtable-equivalence-function}{ \var{hashtable}}{procedure}}

Returns the equivalence function used by
\var{hashtable} to compare keys.  For hashtables
created with {\cf make-eq-hashtable} and {\cf make-eqv-hashtable},
returns {\cf eq?} and {\cf eqv?} respectively.
\end{entry}

\begin{entry}{\proto{hashtable-hash-function}{ \var{hashtable}}{procedure}}

Returns the hash function used by \var{hashtable}.
For hashtables created by {\cf make-eq-hashtable}
or {\cf make-\hp{}eqv-\hp{}hashtable}, \schfalse{} is returned.
\end{entry}

\begin{entry}{\proto{hashtable-mutable?}{ \var{hashtable}}{procedure}}

Returns \schtrue{} if \var{hashtable} is mutable, otherwise \schfalse{}.
\end{entry}

\section{Hash functions}

The {\cf equal-hash}, {\cf string-hash}, and {\cf string-ci-hash}
procedures of this section are acceptable as the hash functions of
a hashtable only if the keys on which they are called are not mutated
while they remain in use as keys in the hashtable.

\begin{entry}{\proto{equal-hash}{ \var{obj}}{procedure}}

Returns an integer hash value for \var{obj}, based on
its structure and current contents.  This hash function is suitable
for use with {\cf equal?} as an equivalence function.

\begin{note}
  Like {\cf equal?}, the {\cf equal-hash} procedure must always
  terminate, even if its arguments contain cycles.
\end{note}
\end{entry}

\begin{entry}{\proto{string-hash}{ \var{string}}{procedure}}

Returns an integer hash value for \var{string}, based on
its current contents.
This hash function is suitable
for use with {\cf string=?} as an equivalence function.
\end{entry}

\begin{entry}{\proto{string-ci-hash}{ \var{string}}{procedure}}

Returns an integer hash value for \var{string} based on
its current contents, ignoring case.
This hash function is suitable
for use with {\cf string-ci=?} as an equivalence function.
\end{entry}

\begin{entry}{\proto{symbol-hash}{ \var{symbol}}{procedure}}

Returns an integer hash value for \var{symbol}.
\end{entry}

%%% Local Variables:
%%% mode: latex
%%% TeX-master: "r6rs-lib"
%%% End:


\chapter{Lattices of Subgroups in Special Cases}
%%%%%%%Case8
\begin{figure}[H]
\centering
\tikzstyle{every node}=[draw=black,thick,anchor=west]
\tikzstyle{selected}=[fill=gray!30]
\begin{tikzpicture}[%
  grow via three points={one child at (1.5,-1) and
  two children at (1.5,-1) and (1.5,-2)},
  edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
  \node {$G_8$}
    child { node [selected]{$[ 5, 389, 4 ]$}}
    child [missing] {}
    child { node {$[  5, 607, 4  ]$}
		child{ node[selected]{$[ 5, 98, 28 ]$}}
		child{ node[selected]{$[ 5, 174, 2 ]$}}
		child{ node{$ [ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
    }
    child[missing]{}
    child[missing]{}
    child [missing] {}
    child [missing] {}
    child [missing] {}
    child { node {$[ 5, 917, 3 ]$}
		child{ node[selected]{$[ 5, 57, 8 ]$}}
		child{ node[selected]{$ [ 5, 901, 3 ]$}}
    }
    child[missing]{}
    child[missing]{}
    child { node{$[ 5, 952, 4  ] $}
		child{ node[selected]{$[ 5, 174, 5 ]$}}
		child{ node{$ [ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
		child[missing]{}
		child{ node[selected]{$ [ 5, 901, 3 ]$}}
    }
    child [missing] {}
    child [missing] {}
    child [missing] {};
\end{tikzpicture}
\caption{Conjugacy classes of subgroups of $G_8$. Algorithm (1) works for the gray ones.}
\label{G8Lat}
\end{figure}

%%%%%%Case9

\begin{figure}[H]
\centering
\tikzstyle{every node}=[draw=black,thick,anchor=west]
\tikzstyle{selected}=[fill=gray!30]
\begin{tikzpicture}[%
  grow via three points={one child at (1.5,-1) and
  two children at (1.5,-1) and (1.5,-2)},
  edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
  \node {$G_9$}
    child { node [selected]{$[ 5, 389, 4 ]$}}
    child [missing] {}
    child { node {$[ 5, 607, 9 ]$}
		child{ node[selected]{$[ 5, 98, 28 ]$}}
		child{ node[selected]{$[ 5, 174, 5 ]$}}
		child{ node{$ [ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
    }
    child[missing]{}
    child[missing]{}
    child [missing] {}
    child [missing] {}
    child [missing] {}
    child { node {$[ 5, 917, 4 ]$}
		child{ node[selected]{$[ 5, 57, 8 ]$}}
		child{ node[selected]{$ [ 5, 901, 3 ]$}}
    }
    child[missing]{}
    child[missing]{}
    child { node{$[ 5, 952, 2 ] $}
		child{ node[selected]{$[ 5, 174, 2 ]$}}
		child{ node{$ [ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
		child[missing]{}
		child{ node[selected]{$ [ 5, 901, 3 ]$}}
    }
    child [missing] {}
    child [missing] {}
    child [missing] {};
\end{tikzpicture}
\caption{Conjugacy classes of subgroups of $G_9$. Algorithm (1) works for the gray ones.}
\label{G9Lat}
\end{figure}

%%%%%%%Case10

\begin{figure}[H]
\centering
\tikzstyle{every node}=[draw=black,thick,anchor=west]
\tikzstyle{selected}=[fill=gray!30]
\begin{tikzpicture}[%
  grow via three points={one child at (2,-1) and
  two children at (2,-1) and (2,-2)},
  edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
  \node {$G_{10}$}
    child { node {$[ 5, 391, 4 ]$}
		child{ node[selected]{$[ 5, 18, 28 ]$}}
		child{ node{$[ 5, 173, 4 ]$}
			child{ node[selected]{$[ 5, 6, 3 ]$}}
			child{ node[selected]{$[ 5, 164, 2 ]$}}
		}
		child[missing]{}
	child[missing]{}
		child{ node[selected]{$[ 5, 174, 2 ]$}}
		child{ node{$[ 5, 461, 4 ] $}
			child{ node[selected]{$[ 5, 6, 3 ]$}}
			child{ node[selected]{$[ 5, 164, 2 ]$}}
		}
    }
	child[missing]{}
	    child [missing] {}
    child [missing] {}
    child [missing] {}
    child [missing] {}
    child [missing] {}
    child [missing] {}
    child { node {$[ 5, 608, 4 ]$}
		child{ node[selected]{$[ 5, 99, 57 ]$}}
		child{ node{$[ 5, 173, 4 ]$}
			child{ node[selected]{$[ 5, 6, 3 ]$}}
			child{ node[selected]{$[ 5, 164, 2 ]$}}
		}
		child[missing]{}
		child[missing]{}
		child{ node{$[ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
    }
    child[missing]{}
    child[missing]{}
    child [missing] {}
       child [missing] {}
          child [missing] {}
    child { node[selected]{$[ 5, 918, 4 ]$}
    }
    child { node{$[ 5, 952, 2 ] $}
		child{ node[selected]{$[ 5, 174, 2 ]$}}
		child{ node{$ [ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
		child[missing]{}
		child{ node[selected]{$ [ 5, 901, 3 ]$}}
    }
    child [missing] {}
    child [missing] {}
    child [missing] {};
\end{tikzpicture}
\caption{Conjugacy classes of subgroups of $G_{10}$. Algorithm (1) works for the gray ones.}
\label{G10Lat}
\end{figure}
\bigskip

%%%%%%%%%Case15

\begin{figure}[H]
\centering
\tikzstyle{every node}=[draw=black,thick,anchor=west]
\tikzstyle{selected}=[fill=gray!30]
\begin{tikzpicture}[%
  grow via three points={one child at (2,-0.8) and
  two children at (2,-0.8) and (2,-1.6)},
  edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
  \node {$G15$}
    child { node [selected]{$[ 5, 81, 54 ]$}
    }
    child { node {$[ 5, 391, 4 ]$}
		child{ node[selected]{$[ 5, 18, 28 ]$}}
		child{ node{$[ 5, 173, 4 ]$}
			child{ node[selected]{$[ 5, 6, 3 ]$}}
			child{ node[selected]{$[ 5, 164, 2 ]$}}
		}
		child[missing]{}
	child[missing]{}
		child{ node[selected]{$[ 5, 174, 2 ]$}}
		child{ node{$[ 5, 461, 4 ] $}
			child{ node[selected]{$[ 5, 6, 3 ]$}}
			child{ node[selected]{$[ 5, 164, 2 ]$}}
		}
    }
    child[missing]{}
    child[missing]{}
    child [missing] {}
       child [missing] {}
          child [missing] {}
      child [missing] {}
        child [missing] {}
    child { node {$[ 5, 606, 4 ]$}
		child{ node[selected]{$[ 5, 22, 14 ]$}}
		child{ node{$[ 5, 461, 4 ]$}
			child{ node[selected]{$[ 5, 6, 3 ]$}}
			child{ node[selected]{$[ 5, 164, 2 ]$}}
		}
		child[missing]{}
		child[missing]{}
		child{ node{$[ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
    }
	    child [missing] {}
    child [missing] {}
    child [missing] {}
    child [missing] {}
    child [missing] {}
    child [missing] {}
     child { node {$[  5, 607, 4  ]$}
		child{ node[selected]{$[ 5, 98, 28 ]$}}
		child{ node[selected]{$[ 5, 174, 2 ]$}}
		child{ node{$ [ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
    }
     child [missing] {}
    child [missing] {}
    child [missing] {}
     child [missing] {}
     child { node {$[ 5, 608, 4 ]$}
		child{ node[selected]{$[ 5, 99, 57 ]$}}
		child{ node{$[ 5, 173, 4 ]$}
			child{ node[selected]{$[ 5, 6, 3 ]$}}
			child{ node[selected]{$[ 5, 164, 2 ]$}}
		}
		child[missing]{}
		child[missing]{}
		child{ node{$[ 5, 580, 4 ]$}
			child{ node[selected]{$[ 5, 19, 14 ]$}}
			child{ node[selected]{$ [ 5, 164, 2 ]$}}
		}
		child[missing]{}
    };

\end{tikzpicture}
\caption{Conjugacy classes of subgroups of $G_{15}$. Algorithm (1) works for the gray ones.}
\end{figure}


\documentclass[12pt]{amsart}
\usepackage{geometry} % see geometry.pdf on how to lay out the page. There's lots.
\usepackage{bsymb}
\usepackage{unitb}
\usepackage{calculational}
\usepackage{ulem}
\usepackage{hyperref}
\normalem
\geometry{a4paper} % or letter or a5paper or ... etc
% \geometry{landscape} % rotated page geometry

% See the ``Article customise'' template for some common customisations

\title{}
\author{}
\date{} % delete this line to display the current date

%%% BEGIN DOCUMENT
\setcounter{tocdepth}{4}
\begin{document}

\maketitle
\tableofcontents

\newcommand{\Train}{\text{TRAIN}}
\newcommand{\Blk}{\text{BLK}}

\section{Initial model}
\begin{machine}{m0}

\newevent{evt}{evt}

% \removecoarse{evt}{default} % \weakento{evt}{default}{c0}

\cschedule{evt}{c0}{\true}

\transient{evt}{tr0}{ \true }

\progress{p0}{ \true }{ \true }

\refine{p0}{discharge}{tr0}{}

\end{machine}

\section{First refinement}
\begin{machine}{m1}

\refines{m0}\removecoarse{evt}{c0}

\replace{evt}{c1}{p0}{saf0}

\safety{saf0}{ \true }{ \true }

\cschedule{evt}{c1}{\true}

\end{machine}{m1}

\section{Second refinement}

\begin{machine}{m2}

\refines{m1}

\replacefine{evt}{p0}

\fschedule{evt}{f2}{\true}

\end{machine}

\end{document}


\section{Development of Methods}

\hspace*{0.3cm} All of the methods outlined in preceding chapters were developed in \textit{RStudio} and are stored in a public code repository on \textit{GitHub}. Initial development and testing of all the packages and libraries was completed using \textit{R Markdown} notebooks, which allow for coding in chunks with explanation in markdown text. These netbooks are kept in the code repo to serve as more explanatory documents demonstrating how all the scripts and datasets work in conjunction with one another. \\

The resulting and final scripts are kept in the code repository as well. The final scripts were developed to use the output from one another and to produce intermediate outputs so that small studies could be conducted. How the scripts work with one another is mapped out in the ``Script Map" in Figure 3.1.\\

\begin{figure}
\includegraphics[width=6in]{Content/Images/ScriptMap.png}\\
\caption{Development script map that describes how all modules work together from data ingest to final analysis.}
\end{figure}

 The Script Map shows how these R scripts can be used for either standard computation, in the case of the NHTSA dataset, or parallel computation, as was used in the reddit dataset. Parallel computation was completed through use of the \texttt{\{furrr\}}, an R package that makes parallization of functions similar to mapping functions from the popular \texttt{\{purrr\}} package. The \texttt{\{furrr\}} package works by splitting the computation among ``workers", or cores, in the CPU. This takes full advantage of the processing capabilities of the machine; instead of letting 1 core do all of the work, we can use many more cores to complete the computation faster. Implementation of a parallel option was necessary because computational time for a list of graph objects grows rapidly as the number of graphs in the list increases. A small study was conducted on the reddit dataset to plot this relationship.\\

In Figure 3.2, there is strong evidence that the computation time follows $O(e^n)$, or worse. For this reason, the parallel workflow was developed\textemdash to handle datasets with a large number of documents to compare. Even though the amount of text per document in the NHTSA dataset is much larger than the amount of text per document in the reddit dataset, the reddit dataset takes longer to compute than the NHTSA dataset. \\

\begin{figure}
\includegraphics[width=6in]{Content/Images/edgeHistTimePlot.png}\\
\caption{Small study on computation time as a function of the number of graphs in list to be processed. Data from reddit used.}
\end{figure}

Referring back to the Script Map, either workflow is decomposed into four steps: Data Ingest, Data Reform, Graph Kernel Similarity, and Clustering. This is the basic work flow developed here. There are some things to note in the Script Map.\\

First, vertex labels need corrected to work with the \texttt{\{graphkernels\}} package. This is done through reassignment of all text vertex labels to be a scalar value. The labels are mapped from the union of two graphs' labels to values ranging from $1-n$, where $n$ is the size of the text label set from the union. This script is run within the \texttt{convert\char`_to\char`_graphs.R} script, so it operates on two graphs at a time as it iterates through the whole graph set of concern.\\

Second, the scripts routed through \texttt{\{furrr\}} are functions which are parallelized in the \texttt{convert\char`_to\char`_graphs.R} and \texttt{compute\char`_kernel.R} scripts. These parallelized scripts are done so through a function being executed with no argument, so the dataset which they consider is hard coded into the script. This is not good from a reproducibility aspect, but it is well documented in the code and in the repository, and has reproducible results. \\

Third, the \texttt{splitdata.R} function is there to simply split up the dataset from reddit, which would be too large to store in the repository on GitHub. This segmentation of the data still allows for workflow and reproducibility, since the datasets can all be read in to the environment all the same. \\

Lastly, a KDE clustering analysis was not performed on the NHTSA dataset, as that dataset has few observations ($N=48$) and will not perform well with the density estimation methods developed here; there would be too few observations to generate a number of local maxima/minima needed to apply the clustering methods developed. Results here were not included, but the scripts and methods are available in the Appendix and code repository.\\


\section{\src{dyn_horiz_adv_limiter}}
\label{dyn_horiz_adv_limiter}

\subsection{Description}

Kernel \src{dyn_horiz_adv_limiter} is taken from the original subroutine
\src{horizontal_limiter_thuburn} in \NICAM.
%
This subroutine is originally defined in \src{mod_src_tracer}, that
is to contain several subroutines for tracer advection.
%
Subroutine \src{horizontal_limiter_thuburn} is to ensure distribution of
tracer quantities' monotonicity in advection scheme, using the flux
limitter proposed by \cite{Thuburn:1996in}.
%
This subroutine is for horizontal advection only and vertical advection
is treated by other subroutine \src{vertical_limiter_thuburn}, which is
also kernelized in this package (See \autoref{dyn_vert_adv_limiter}).
%
In NICAM, a third order upwind scheme proposed by \cite{Miura:2007bs} is used
for the horizontal tracer advection on the icosahedral grid.
See section 4. in \cite{Tomita2010ecmwf} for details of the tracer scheme in \NICAM, too.

\subsection{Discretization and code}

Argument lists and local variables definition part of this subroutine is
as follows.

\begin{LstF90}[name=horizontal_limiter_thuburn]
subroutine horizontal_limiter_thuburn( &
     q_a,    q_a_pl,  &
     q,      q_pl,    &
     d,      d_pl,    &
     ch,     ch_pl,   &
     cmask,  cmask_pl, &
     Qout_prev, Qout_prev_pl, & ! KERNEL
     Qout_post, Qout_post_pl  ) ! KERNEL
!ESC!    use mod_const, only: &
!ESC!       CONST_HUGE, &
!ESC!       CONST_EPS
!ESC!    use mod_adm, only: &
!ESC!       ADM_have_pl,    &
!ESC!       ADM_have_sgp,   &
!ESC!       ADM_lall,       &
!ESC!       ADM_lall_pl,    &
!ESC!       ADM_gall,       &
!ESC!       ADM_gall_pl,    &
!ESC!       ADM_kall,       &
!ESC!       ADM_gall_1d,    &
!ESC!       ADM_gmin,       &
!ESC!       ADM_gmax,       &
!ESC!       ADM_gslf_pl,    &
!ESC!       ADM_gmin_pl,    &
!ESC!       ADM_gmax_pl
!ESC!    use mod_comm, only: &
!ESC!       COMM_data_transfer
!ESC!    implicit none

  real(RP), intent(inout) :: q_a     (ADM_gall   ,ADM_kall,ADM_lall   ,6)
  real(RP), intent(inout) :: q_a_pl  (ADM_gall_pl,ADM_kall,ADM_lall_pl  )
  real(RP), intent(in)    :: q       (ADM_gall   ,ADM_kall,ADM_lall     )
  real(RP), intent(in)    :: q_pl    (ADM_gall_pl,ADM_kall,ADM_lall_pl  )
  real(RP), intent(in)    :: d       (ADM_gall   ,ADM_kall,ADM_lall     )
  real(RP), intent(in)    :: d_pl    (ADM_gall_pl,ADM_kall,ADM_lall_pl  )
  real(RP), intent(in)    :: ch      (ADM_gall   ,ADM_kall,ADM_lall   ,6)
  real(RP), intent(in)    :: ch_pl   (ADM_gall_pl,ADM_kall,ADM_lall_pl  )
  real(RP), intent(in)    :: cmask   (ADM_gall   ,ADM_kall,ADM_lall   ,6)
  real(RP), intent(in)    :: cmask_pl(ADM_gall_pl,ADM_kall,ADM_lall_pl  )
  real(RP), intent(out)   :: Qout_prev   (ADM_gall   ,ADM_kall,ADM_lall   ,2 ) ! before communication (for check)
  real(RP), intent(out)   :: Qout_prev_pl(ADM_gall_pl,ADM_kall,ADM_lall_pl,2 ) !
  real(RP), intent(in)    :: Qout_post   (ADM_gall   ,ADM_kall,ADM_lall   ,2 ) ! after communication (additional input)
  real(RP), intent(in)    :: Qout_post_pl(ADM_gall_pl,ADM_kall,ADM_lall_pl,2 ) !

  real(RP) :: q_min_AI, q_min_AIJ, q_min_AJ, q_min_pl
  real(RP) :: q_max_AI, q_max_AIJ, q_max_AJ, q_max_pl

  real(RP) :: qnext_min   , qnext_min_pl
  real(RP) :: qnext_max   , qnext_max_pl
  real(RP) :: Cin_sum     , Cin_sum_pl
  real(RP) :: Cout_sum    , Cout_sum_pl
  real(RP) :: CQin_max_sum, CQin_max_sum_pl
  real(RP) :: CQin_min_sum, CQin_min_sum_pl

  integer, parameter :: I_min = 1
  integer, parameter :: I_max = 2
  real(RP) :: Qin    (ADM_gall   ,ADM_kall,ADM_lall   ,2,6)
  real(RP) :: Qin_pl (ADM_gall_pl,ADM_kall,ADM_lall_pl,2,2)
  real(RP) :: Qout   (ADM_gall   ,ADM_kall,ADM_lall   ,2  )
  real(RP) :: Qout_pl(ADM_gall_pl,ADM_kall,ADM_lall_pl,2  )

  real(RP) :: ch_masked1
  real(RP) :: ch_masked2
  real(RP) :: ch_masked3
  real(RP) :: ch_masked4
  real(RP) :: ch_masked5
  real(RP) :: ch_masked6
  real(RP) :: ch_masked
  real(RP) :: zerosw

  integer  :: gmin, gmax, kall, iall
  real(RP) :: EPS, BIG

  integer  :: ij
  integer  :: ip1j, ijp1, ip1jp1, ip2jp1
  integer  :: im1j, ijm1

  integer  :: i, j, k, l, n, v
  !---------------------------------------------------------------------------

\end{LstF90}
%
\src{q_a} is $q$ at the edge of hexagonal control cell, which modified by the flux limiter.
%
Note that \src{q_a} has 4-th dimension and its size is 6, that specifies
6 edges of hexagon control volume.
%
\src{q} is $q$ at grid point,
%
\src{d} is a correction factor derived from an artificial viscosity for the total density.
%
\src{ch} is Courant number, \src{cmask} is upwind direction mask.
%
In original subroutine in NICAM, \src{Qout_prev} and \src{Qout_post} are not exist.
These additional arguments is prepared to avoid halo communication, which appeared in the middle of this scheme.
The detail is discussed below.

The first section of the subroutine is as follows.

\begin{LstF90}[name=horizontal_limiter_thuburn,firstnumber=last]
  call DEBUG_rapstart('____horizontal_adv_limiter')

  gmin = ADM_gmin
  gmax = ADM_gmax
  kall = ADM_kall
  iall = ADM_gall_1d

  EPS  = CONST_EPS
  BIG  = CONST_HUGE

  do l = 1, ADM_lall
     !$omp parallel default(none), &
     !$omp private(i,j,k,ij,ip1j,ip1jp1,ijp1,im1j,ijm1,ip2jp1,                        &
     !$omp         q_min_AI,q_min_AIJ,q_min_AJ,q_max_AI,q_max_AIJ,q_max_AJ,zerosw,    &
     !$omp         ch_masked1,ch_masked2,ch_masked3,ch_masked4,ch_masked5,ch_masked6, &
     !$omp         qnext_min,qnext_max,Cin_sum,Cout_sum,CQin_min_sum,CQin_max_sum),   &
     !$omp shared(l,ADM_have_sgp,gmin,gmax,kall,iall,q,cmask,d,ch,Qin,Qout,EPS,BIG)
     do k = 1, kall
        !---< (i) define inflow bounds, eq.(32)&(33) >---
!OCL XFILL
        !$omp do
        do j = gmin-1, gmax
        do i = gmin-1, gmax
           ij     = (j-1)*iall + i
           ip1j   = ij + 1
           ip1jp1 = ij + iall + 1
           ijp1   = ij + iall
           im1j   = ij - 1
           ijm1   = ij - iall

           im1j   = max( im1j  , 1 )
           ijm1   = max( ijm1  , 1 )

           q_min_AI  = min( q(ij,k,l), q(ijm1,k,l), q(ip1j,k,l), q(ip1jp1,k,l) )
           q_max_AI  = max( q(ij,k,l), q(ijm1,k,l), q(ip1j,k,l), q(ip1jp1,k,l) )
           q_min_AIJ = min( q(ij,k,l), q(ip1j,k,l), q(ip1jp1,k,l), q(ijp1,k,l) )
           q_max_AIJ = max( q(ij,k,l), q(ip1j,k,l), q(ip1jp1,k,l), q(ijp1,k,l) )
           q_min_AJ  = min( q(ij,k,l), q(ip1jp1,k,l), q(ijp1,k,l), q(im1j,k,l) )
           q_max_AJ  = max( q(ij,k,l), q(ip1jp1,k,l), q(ijp1,k,l), q(im1j,k,l) )

           Qin(ij,    k,l,I_min,1) = (        cmask(ij,k,l,1) ) * q_min_AI &
                                   + ( 1.0_RP-cmask(ij,k,l,1) ) * BIG
           Qin(ip1j,  k,l,I_min,4) = (        cmask(ij,k,l,1) ) * BIG      &
                                   + ( 1.0_RP-cmask(ij,k,l,1) ) * q_min_AI
           Qin(ij,    k,l,I_max,1) = (        cmask(ij,k,l,1) ) * q_max_AI &
                                   + ( 1.0_RP-cmask(ij,k,l,1) ) * (-BIG)
           Qin(ip1j,  k,l,I_max,4) = (        cmask(ij,k,l,1) ) * (-BIG)   &
                                   + ( 1.0_RP-cmask(ij,k,l,1) ) * q_max_AI

           Qin(ij,    k,l,I_min,2) = (        cmask(ij,k,l,2) ) * q_min_AIJ &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * BIG
           Qin(ip1jp1,k,l,I_min,5) = (        cmask(ij,k,l,2) ) * BIG       &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * q_min_AIJ
           Qin(ij,    k,l,I_max,2) = (        cmask(ij,k,l,2) ) * q_max_AIJ &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * (-BIG)
           Qin(ip1jp1,k,l,I_max,5) = (        cmask(ij,k,l,2) ) * (-BIG)    &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * q_max_AIJ

           Qin(ij,    k,l,I_min,3) = (        cmask(ij,k,l,3) ) * q_min_AJ &
                                   + ( 1.0_RP-cmask(ij,k,l,3) ) * BIG
           Qin(ijp1,  k,l,I_min,6) = (        cmask(ij,k,l,3) ) * BIG      &
                                   + ( 1.0_RP-cmask(ij,k,l,3) ) * q_min_AJ
           Qin(ij,    k,l,I_max,3) = (        cmask(ij,k,l,3) ) * q_max_AJ &
                                   + ( 1.0_RP-cmask(ij,k,l,3) ) * (-BIG)
           Qin(ijp1,  k,l,I_max,6) = (        cmask(ij,k,l,3) ) * (-BIG)   &
                                   + ( 1.0_RP-cmask(ij,k,l,3) ) * q_max_AJ
        enddo
        enddo
        !$omp end do

        if ( ADM_have_sgp(l) ) then
           !$omp master
           j = gmin-1
           i = gmin-1

           ij     = (j-1)*iall + i
           ijp1   = ij + iall
           ip1jp1 = ij + iall + 1
           ip2jp1 = ij + iall + 2

           q_min_AIJ = min( q(ij,k,l), q(ip1jp1,k,l), q(ip2jp1,k,l), q(ijp1,k,l) )
           q_max_AIJ = max( q(ij,k,l), q(ip1jp1,k,l), q(ip2jp1,k,l), q(ijp1,k,l) )

           Qin(ij,    k,l,I_min,2) = (        cmask(ij,k,l,2) ) * q_min_AIJ &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * BIG
           Qin(ip1jp1,k,l,I_min,5) = (        cmask(ij,k,l,2) ) * BIG       &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * q_min_AIJ
           Qin(ij,    k,l,I_max,2) = (        cmask(ij,k,l,2) ) * q_max_AIJ &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * (-BIG)
           Qin(ip1jp1,k,l,I_max,5) = (        cmask(ij,k,l,2) ) * (-BIG)    &
                                   + ( 1.0_RP-cmask(ij,k,l,2) ) * q_max_AIJ
           !$omp end master
        endif

\end{LstF90}

The first section above and second section below are in long $l$- and $k$- double loop.
In that there are 2 main $i,j$-double loops and one \src{IF} clause and a
small $i,j$-loop.
%
The $i,j$-loop(l.102) calculates the inflow bounds.
First, minimum and maximum of $q$ at arc points (specified by \src{AI},
\src{AIJ} and \src{AJ}) are set, then actual bound of inflow \src{Qin} is
calcurated.
%
\src{IF} clause at l.151 is a treatment for the singular points.

Second section is as follows.

\begin{LstF90}[name=horizontal_limiter_thuburn,firstnumber=last]
        !---< (iii) define allowable range of q at next step, eq.(42)&(43) >---
!OCL XFILL
        !$omp do
        do j = gmin, gmax
        do i = gmin, gmax
           ij = (j-1)*iall + i

           qnext_min = min( q(ij,k,l),           &
                            Qin(ij,k,l,I_min,1), &
                            Qin(ij,k,l,I_min,2), &
                            Qin(ij,k,l,I_min,3), &
                            Qin(ij,k,l,I_min,4), &
                            Qin(ij,k,l,I_min,5), &
                            Qin(ij,k,l,I_min,6)  )

           qnext_max = max( q(ij,k,l),           &
                            Qin(ij,k,l,I_max,1), &
                            Qin(ij,k,l,I_max,2), &
                            Qin(ij,k,l,I_max,3), &
                            Qin(ij,k,l,I_max,4), &
                            Qin(ij,k,l,I_max,5), &
                            Qin(ij,k,l,I_max,6)  )

           ch_masked1 = min( ch(ij,k,l,1), 0.0_RP )
           ch_masked2 = min( ch(ij,k,l,2), 0.0_RP )
           ch_masked3 = min( ch(ij,k,l,3), 0.0_RP )
           ch_masked4 = min( ch(ij,k,l,4), 0.0_RP )
           ch_masked5 = min( ch(ij,k,l,5), 0.0_RP )
           ch_masked6 = min( ch(ij,k,l,6), 0.0_RP )

           Cin_sum      = ch_masked1 &
                        + ch_masked2 &
                        + ch_masked3 &
                        + ch_masked4 &
                        + ch_masked5 &
                        + ch_masked6

           Cout_sum     = ch(ij,k,l,1) - ch_masked1 &
                        + ch(ij,k,l,2) - ch_masked2 &
                        + ch(ij,k,l,3) - ch_masked3 &
                        + ch(ij,k,l,4) - ch_masked4 &
                        + ch(ij,k,l,5) - ch_masked5 &
                        + ch(ij,k,l,6) - ch_masked6

           CQin_min_sum = ch_masked1 * Qin(ij,k,l,I_min,1) &
                        + ch_masked2 * Qin(ij,k,l,I_min,2) &
                        + ch_masked3 * Qin(ij,k,l,I_min,3) &
                        + ch_masked4 * Qin(ij,k,l,I_min,4) &
                        + ch_masked5 * Qin(ij,k,l,I_min,5) &
                        + ch_masked6 * Qin(ij,k,l,I_min,6)

           CQin_max_sum = ch_masked1 * Qin(ij,k,l,I_max,1) &
                        + ch_masked2 * Qin(ij,k,l,I_max,2) &
                        + ch_masked3 * Qin(ij,k,l,I_max,3) &
                        + ch_masked4 * Qin(ij,k,l,I_max,4) &
                        + ch_masked5 * Qin(ij,k,l,I_max,5) &
                        + ch_masked6 * Qin(ij,k,l,I_max,6)

           zerosw = 0.5_RP - sign(0.5_RP,abs(Cout_sum)-EPS) ! if Cout_sum = 0, sw = 1

           Qout(ij,k,l,I_min) = ( q(ij,k,l) - CQin_max_sum - qnext_max*(1.0_RP-Cin_sum-Cout_sum+d(ij,k,l)) ) &
                              / ( Cout_sum + zerosw ) * ( 1.0_RP - zerosw )                                         &
                              + q(ij,k,l) * zerosw
           Qout(ij,k,l,I_max) = ( q(ij,k,l) - CQin_min_sum - qnext_min*(1.0_RP-Cin_sum-Cout_sum+d(ij,k,l)) ) &
                              / ( Cout_sum + zerosw ) * ( 1.0_RP - zerosw )                                         &
                              + q(ij,k,l) * zerosw
        enddo
        enddo
        !$omp end do

!OCL XFILL
        !$omp do
        do j = 1, iall
        do i = 1, iall
           if (      i < gmin .OR. i > gmax &
                .OR. j < gmin .OR. j > gmax ) then
              ij = (j-1)*iall + i

              Qout(ij,k,l,I_min) = q(ij,k,l)
              Qout(ij,k,l,I_min) = q(ij,k,l)
              Qout(ij,k,l,I_max) = q(ij,k,l)
              Qout(ij,k,l,I_max) = q(ij,k,l)
           endif
        enddo
        enddo
        !$omp end do

     enddo ! k loop
     !$omp end parallel
  enddo ! l loop

\end{LstF90}

There are two $i,j$-double loops.
%
In the first one, allowable range of $q$ is defined as \src{qnext_min},
\src{qnext_max}, then \src{Qout} is calculated.
%
And the second double loop is to set halo region.

The third section is as follows.
This section is for the pole region, doing almost the same calculation
with the normal region.

\begin{LstF90}[name=horizontal_limiter_thuburn,firstnumber=last,breaklines=false,prebreak={}]
  if ( ADM_have_pl ) then
     n = ADM_gslf_pl

     do l = 1, ADM_lall_pl
        do k = 1, ADM_kall
           do v = ADM_gmin_pl, ADM_gmax_pl
              ij   = v
              ijp1 = v + 1
              ijm1 = v - 1
              if( ijp1 == ADM_gmax_pl+1 ) ijp1 = ADM_gmin_pl
              if( ijm1 == ADM_gmin_pl-1 ) ijm1 = ADM_gmax_pl

              q_min_pl = min( q_pl(n,k,l), q_pl(ij,k,l), q_pl(ijm1,k,l), q_pl(ijp1,k,l) )
              q_max_pl = max( q_pl(n,k,l), q_pl(ij,k,l), q_pl(ijm1,k,l), q_pl(ijp1,k,l) )

              Qin_pl(ij,k,l,I_min,1) = (        cmask_pl(ij,k,l) ) * q_min_pl &
                                     + ( 1.0_RP-cmask_pl(ij,k,l) ) * BIG
              Qin_pl(ij,k,l,I_min,2) = (        cmask_pl(ij,k,l) ) * BIG      &
                                     + ( 1.0_RP-cmask_pl(ij,k,l) ) * q_min_pl
              Qin_pl(ij,k,l,I_max,1) = (        cmask_pl(ij,k,l) ) * q_max_pl &
                                     + ( 1.0_RP-cmask_pl(ij,k,l) ) * (-BIG)
              Qin_pl(ij,k,l,I_max,2) = (        cmask_pl(ij,k,l) ) * (-BIG)   &
                                     + ( 1.0_RP-cmask_pl(ij,k,l) ) * q_max_pl
           enddo

           qnext_min_pl = q_pl(n,k,l)
           qnext_max_pl = q_pl(n,k,l)
           do v = ADM_gmin_pl, ADM_gmax_pl
              qnext_min_pl = min( qnext_min_pl, Qin_pl(v,k,l,I_min,1) )
              qnext_max_pl = max( qnext_max_pl, Qin_pl(v,k,l,I_max,1) )
           enddo

           Cin_sum_pl      = 0.0_RP
           Cout_sum_pl     = 0.0_RP
           CQin_max_sum_pl = 0.0_RP
           CQin_min_sum_pl = 0.0_RP
           do v = ADM_gmin_pl, ADM_gmax_pl
              ch_masked = cmask_pl(v,k,l) * ch_pl(v,k,l)

              Cin_sum_pl      = Cin_sum_pl      + ch_masked
              Cout_sum_pl     = Cout_sum_pl     - ch_masked + ch_pl (v,k,l)
              CQin_min_sum_pl = CQin_min_sum_pl + ch_masked * Qin_pl(v,k,l,I_min,1)
              CQin_max_sum_pl = CQin_max_sum_pl + ch_masked * Qin_pl(v,k,l,I_max,1)
           enddo

           zerosw = 0.5_RP - sign(0.5_RP,abs(Cout_sum_pl)-EPS) ! if Cout_sum_pl = 0, sw = 1

           Qout_pl(n,k,l,I_min) = ( q_pl(n,k,l) - CQin_max_sum_pl - qnext_max_pl*(1.0_RP-Cin_sum_pl-Cout_sum_pl+d_pl(n,k,l)) ) &
                                / ( Cout_sum_pl + zerosw ) * ( 1.0_RP - zerosw )                                               &
                                + q_pl(n,k,l) * zerosw
           Qout_pl(n,k,l,I_max) = ( q_pl(n,k,l) - CQin_min_sum_pl - qnext_min_pl*(1.0_RP-Cin_sum_pl-Cout_sum_pl+d_pl(n,k,l)) ) &
                                / ( Cout_sum_pl + zerosw ) * ( 1.0_RP - zerosw )                                               &
                                + q_pl(n,k,l) * zerosw

        enddo
     enddo
  endif

\end{LstF90}

In the original subroutine, halo exchange using communication subroutine
\src{COMM_data_transfer} here.
%
In kernelization process, this comunication is omitted.
%
Values to be sent is output for the purpose of validation, and values to
be received are given as an argument, read from input data file in the
main program of this kernel program.

\begin{LstF90}[name=horizontal_limiter_thuburn,firstnumber=last]
  !#####################################################################KERNEL
  call DEBUG_rapend  ('____horizontal_adv_limiter')
  Qout_pl(ADM_gmin_pl:ADM_gmax_pl,:,:,:) = 0.0_RP

  Qout_prev   (:,:,:,:) = Qout        (:,:,:,:)
  Qout_prev_pl(:,:,:,:) = Qout_pl     (:,:,:,:)
  !call COMM_data_transfer( Qout(:,:,:,:), Qout_pl(:,:,:,:) )
  Qout        (:,:,:,:) = Qout_post   (:,:,:,:)
  Qout_pl     (:,:,:,:) = Qout_post_pl(:,:,:,:)
  call DEBUG_rapstart('____horizontal_adv_limiter')
  !#####################################################################KERNEL

\end{LstF90}

The next section is as follows.

\begin{LstF90}[name=horizontal_limiter_thuburn,firstnumber=last,breaklines=false,prebreak={},basewidth=.43em,xrightmargin=-1em]
  !---- apply inflow/outflow limiter
  do l = 1, ADM_lall
     !$omp parallel do default(none),private(i,j,k,ij,ip1j,ip1jp1,ijp1), &
     !$omp shared(l,gmin,gmax,kall,iall,q_a,cmask,Qin,Qout)
     do k = 1, kall
        do j = gmin-1, gmax
        do i = gmin-1, gmax
           ij     = (j-1)*iall + i
           ip1j   = ij + 1
           ip1jp1 = ij + iall + 1
           ijp1   = ij + iall

           q_a(ij,k,l,1) = (        cmask(ij,k,l,1) ) * min( max( q_a(ij,k,l,1), Qin (ij    ,k,l,I_min,1) ), Qin (ij    ,k,l,I_max,1) ) &
                         + ( 1.0_RP-cmask(ij,k,l,1) ) * min( max( q_a(ij,k,l,1), Qin (ip1j  ,k,l,I_min,4) ), Qin (ip1j  ,k,l,I_max,4) )
           q_a(ij,k,l,1) = (        cmask(ij,k,l,1) ) * max( min( q_a(ij,k,l,1), Qout(ip1j  ,k,l,I_max  ) ), Qout(ip1j  ,k,l,I_min  ) ) &
                         + ( 1.0_RP-cmask(ij,k,l,1) ) * max( min( q_a(ij,k,l,1), Qout(ij    ,k,l,I_max  ) ), Qout(ij    ,k,l,I_min  ) )
           q_a(ip1j,k,l,4) = q_a(ij,k,l,1)

           q_a(ij,k,l,2) = (        cmask(ij,k,l,2) ) * min( max( q_a(ij,k,l,2), Qin (ij    ,k,l,I_min,2) ), Qin (ij    ,k,l,I_max,2) ) &
                         + ( 1.0_RP-cmask(ij,k,l,2) ) * min( max( q_a(ij,k,l,2), Qin (ip1jp1,k,l,I_min,5) ), Qin (ip1jp1,k,l,I_max,5) )
           q_a(ij,k,l,2) = (        cmask(ij,k,l,2) ) * max( min( q_a(ij,k,l,2), Qout(ip1jp1,k,l,I_max  ) ), Qout(ip1jp1,k,l,I_min  ) ) &
                         + ( 1.0_RP-cmask(ij,k,l,2) ) * max( min( q_a(ij,k,l,2), Qout(ij    ,k,l,I_max  ) ), Qout(ij    ,k,l,I_min  ) )
           q_a(ip1jp1,k,l,5) = q_a(ij,k,l,2)

           q_a(ij,k,l,3) = (        cmask(ij,k,l,3) ) * min( max( q_a(ij,k,l,3), Qin (ij    ,k,l,I_min,3) ), Qin (ij    ,k,l,I_max,3) ) &
                         + ( 1.0_RP-cmask(ij,k,l,3) ) * min( max( q_a(ij,k,l,3), Qin (ijp1  ,k,l,I_min,6) ), Qin (ijp1  ,k,l,I_max,6) )
           q_a(ij,k,l,3) = (        cmask(ij,k,l,3) ) * max( min( q_a(ij,k,l,3), Qout(ijp1  ,k,l,I_max  ) ), Qout(ijp1  ,k,l,I_min  ) ) &
                         + ( 1.0_RP-cmask(ij,k,l,3) ) * max( min( q_a(ij,k,l,3), Qout(ij    ,k,l,I_max  ) ), Qout(ij    ,k,l,I_min  ) )
           q_a(ijp1,k,l,6) = q_a(ij,k,l,3)
        enddo
        enddo
     enddo
     !$omp end parallel do
  enddo

\end{LstF90}%$

In new $l$- and $k$- double loop, there is a $i,j$-double loop, calculating
limitter apllied tracer quantities at each cell faces, i.e. edges of the
hexagonal control volume.
%
Note that only 1st, 2nd, 3rd edge must be calculated, because other 3
edges' value is the same with other grid's 1st, 2nd, or 3rd edge.

The last part of this subroutine is as follows.

\begin{LstF90}[name=horizontal_limiter_thuburn,firstnumber=last]
  if ( ADM_have_pl ) then
     n = ADM_gslf_pl

     do l = 1, ADM_lall_pl
     do k = 1, ADM_kall
     do v = ADM_gmin_pl, ADM_gmax_pl
        q_a_pl(v,k,l) = (        cmask_pl(v,k,l) ) * min(max(q_a_pl(v,k,l), Qin_pl (v,k,l,I_min,1)), Qin_pl (v,k,l,I_max,1)) &
                      + ( 1.0_RP-cmask_pl(v,k,l) ) * min(max(q_a_pl(v,k,l), Qin_pl (v,k,l,I_min,2)), Qin_pl (v,k,l,I_max,2))
        q_a_pl(v,k,l) = (        cmask_pl(v,k,l) ) * max(min(q_a_pl(v,k,l), Qout_pl(v,k,l,I_max  )), Qout_pl(v,k,l,I_min  )) &
                      + ( 1.0_RP-cmask_pl(v,k,l) ) * max(min(q_a_pl(v,k,l), Qout_pl(n,k,l,I_max  )), Qout_pl(n,k,l,I_min  ))
     enddo
     enddo
     enddo
  endif

  call DEBUG_rapend  ('____horizontal_adv_limiter')

  return
end subroutine horizontal_limiter_thuburn
\end{LstF90}

In this part, \src{q_a} in the pole region are calculated, with almost
the same procedure with the normal region.

\subsection{Input data and result}

Max/min/sum of input/output data of the kernel subroutine are output as
a log.
%
Below is an example of \src{$IAB_SYS=Ubuntu-gnu-ompi} case.

\begin{LstLog}
 ### Input ###
 +check[q_a_prev        ] max=  3.1443592333952495E+02,min= -3.3659800625927818E+02,sum=  1.4867286349717416E+07
 +check[q_a_prev_pl     ] max=  7.5768030875957253E+00,min= -5.6211573453294959E-15,sum=  1.5762773946695211E+03
 +check[check_q_a       ] max=  3.0706553064103554E+02,min= -2.3487181740471829E+02,sum=  1.4857441189951191E+07
 +check[check_q_a_pl    ] max=  6.5571419615446418E+00,min= -5.6211573453294959E-15,sum=  1.5683664645246540E+03
 +check[q               ] max=  7.3394566876316425E+00,min=  0.0000000000000000E+00,sum=  2.4959622235983950E+06
 +check[q_pl            ] max=  6.5571419615446418E+00,min=  0.0000000000000000E+00,sum=  1.8536812127383619E+03
 +check[d               ] max=  1.7307542155642468E-05,min= -1.9433424502805651E-05,sum=  6.2175140984963743E-06
 +check[d_pl            ] max=  6.2231074370491122E-06,min= -2.0788320527281462E-05,sum= -1.1073365231406677E-03
 +check[ch              ] max=  2.5115202433354633E-01,min= -2.8726813406847629E-01,sum= -6.8752454869001423E-01
 +check[ch_pl           ] max=  1.9806049765030476E-01,min= -1.2208152245920290E+01,sum= -3.1022849107616275E+02
 +check[cmask           ] max=  1.0000000000000000E+00,min=  0.0000000000000000E+00,sum=  2.1729060000000000E+06
 +check[cmask_pl        ] max=  1.0000000000000000E+00,min=  0.0000000000000000E+00,sum=  2.1800000000000000E+02
 +check[check_Qout_prev ] max=  2.5201719369799781E+05,min= -7.0121044696269173E+05,sum=  4.7462261170534119E+06
 +check[check_Qout_prev_] max=  3.4849498839973911E+01,min= -3.6368061161685844E+01,sum=  7.2956220495347202E+02
 +check[Qout_post       ] max=  2.5201719369799781E+05,min= -7.0121044696269173E+05,sum=  4.7387405453230906E+06
 +check[Qout_post_pl    ] max=  5.9738451374182347E+01,min= -3.6368061161685844E+01,sum=  4.0063169963931473E+03
 ### Output ###
 +check[q_a             ] max=  3.0706553064103554E+02,min= -2.3487181740471829E+02,sum=  1.4857441189951191E+07
 +check[q_a_pl          ] max=  6.5571419615446418E+00,min= -5.6211573453294959E-15,sum=  1.5683664645246540E+03
 +check[Qout_prev       ] max=  2.5201719369799781E+05,min= -7.0121044696269173E+05,sum=  4.7462261170534119E+06
 +check[Qout_prev_pl    ] max=  3.4849498839973911E+01,min= -3.6368061161685844E+01,sum=  7.2956220495347202E+02
 ### Validation : point-by-point diff ###
 +check[check_q_a       ] max=  0.0000000000000000E+00,min=  0.0000000000000000E+00,sum=  0.0000000000000000E+00
 +check[check_q_a_pl    ] max=  0.0000000000000000E+00,min=  0.0000000000000000E+00,sum=  0.0000000000000000E+00
 +check[check_Qout_prev ] max=  0.0000000000000000E+00,min=  0.0000000000000000E+00,sum=  0.0000000000000000E+00
 +check[check_Qout_prev_] max=  0.0000000000000000E+00,min=  0.0000000000000000E+00,sum=  0.0000000000000000E+00
 *** Finish kernel
\end{LstLog}

Check the lines below \src{``Validation : point-by-point diff''} line,
that shows difference between calculated output array and
pre-calculated reference array.
These should be zero or enough small to be acceptable.

There are sample output log files in \file{reference/}
in each kernel program directory, for reference purpose.

\subsection{Sample of perfomance result}

Here's an example of the performance result part of the log output.
Below is an example executed with the machine environment described in \autoref{s:measuring_env}.
%
Note that in this program kernel part is iterated one time.

\begin{LstLog}
 *** Computational Time Report
 *** ID=001 : MAIN_dyn_horiz_adv_limiter       T=     0.180 N=      1
 *** ID=002 : ____horizontal_adv_limiter       T=     0.164 N=      2
\end{LstLog}


\chapter{Introduction}

\section{Closed-loop neuroscience}
\label{sec:BCI}

This thesis discusses signal-processing software for a brain-computer interface (BCI). A BCI is a device that reads out brain activity, which is used to control some automated action. The brain receives feedback about the performed action, either through normal sensory input, or through direct neural stimulation, thereby forming a closed loop.

Most BCI research is focused on restoring or partially replacing a damaged nervous system after a stroke or a traumatic injury \cite{Krucoff2016}. The BCI discussed in this thesis however is used for fundamental brain research into memory \& learning, using laboratory animals such as rats or macaques.
% Many of the techniques applied in this thesis are more generally applicable however.

On a high level, the discussed BCI works as follows (see also \cref{fig:closed-loop}): an electrode is surgically implanted into the animal's brain, where it records the electric field potential. (\Cref{ch:LFP} discusses the origin of this signal). During an experiment, the BCI continuously tests whether it can detect a particular pattern in the recording. (This pattern, called the \emph{sharp wave-ripple}, is discussed in \cref{ch:SWR}). Each detection of the pattern triggers a feedback action. This is often a current injection that temporarily disables the specific brain area where the pattern is recorded from. This prevents the pattern from exercising its function elsewhere in the brain.

This experimental technique enables scientists to test their hypotheses about sharp wave-ripples (SWR's), the affected brain area, and in general, about brain mechanisms such as learning \& memory. (This is discussed further in \cref{ch:science}).

\begin{figure}
\img[0.7]{closed-loop}
\captionn{A closed-loop brain-computer interface in neuroscience}{Arrows show the direction of data flow. ALU: arithmetic logic unit.}
\label{fig:closed-loop}
\end{figure}

\section{Problem statement}
\label{sec:problem}

This thesis investigates whether the current sharp wave-ripple detection algorithm can be improved upon. That is: can we find an online algorithm that detects SWR's with less latency than the current online algorithm, while being at least equally sensitive and precise?

(In \cref{ch:offline,ch:eval}, we discuss these performance criteria in more detail).

\section{Motivation}
\label{sec:motivation}

This question is motivated by two factors. First, current online detection algorithms add a considerable latency: the SWR event is often detected after a third of the event has already passed (see \cref{fig:BPF-outputs,fig:BPF-performance} from \cref{ch:BPF}, where existing online SWR detection algorithms are evaluated). This detection latency might compromise experimental power, as the SWR event may have already exercised its function.

Second, most current detection algorithms make use of only a single voltage signal. Recently however, more and more recordings are being made with \emph{neural probes}, which simultaneously record the voltage at multiple spatially distributed points \cite{Michon2016,Jun2017}. We investigate whether we can use these spatially distributed multichannel recordings to improve online SWR detection.

In \cref{ch:GEVec,ch:RNN}, two existing signal detection techniques are adapted to create new online SWR detection algorithms. These algorithms are analyzed, their parameters are explored, and their performance in online SWR detection is compared with the state-of-the-art. \Cref{ch:conclusions} summarizes the findings, gives recommendations, and discusses limitations of this thesis, as well as directions for further work.


\documentclass[11pt,letterpaper]{article}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage{amsmath,
            amsfonts,
            amssymb}
\usepackage{graphicx}
\usepackage{minted}
\usepackage{float}
\usepackage{hyperref}

\title{\textbf{Solidspy\_DYN}: A Python based finite element code for dynamic analysis of elastic solids using implicit time integration}
\date{}
\author{Juan Gomez and Nicolas Guarin}

\usepackage{cleveref}
\begin{document}
\maketitle

\abstract{This tutorial describes the basic steps required in the construction of a finite element model using the Python based code {\bf Solidspy\_DYN}. The code has been written mainly for teaching purposes and also as a first hand calculator for complex wave propagation problems. {\bf Solidspy\_DYN} is written using Python but also makes extensive use of free open code software as {\bf gmsh} and {\bf Paraview} which are used for pre and post processing. This document describes  the steps involve in the creation of a {\bf gmsh} model to be used within the implicit time integrator. This is achieve through  a sample problem that is geometrically simple yet it involves all the basic parts that conform a {\bf Solidspy\_DYN} analysis. It is assumed that the user is familiar with general {\bf gmsh} syntaxis and features. The user manual is focused in the aspects required to complete a dynamic analysis thus emphasizing the visualization of the solution in the free code {\bf Paraview}.}

\section{Introduction}
The finite element method (FEM) or its variations, is probably the numerical technique of major use in modern engineering to the extent that it is included in a large number of engineering curriculum at the undergraduate and graduate level. Unfortunately many of these courses use as simulation tool specialized and expensive commercial software that, although powerful from a computational point of view is highly limited when used for teaching purposes as these codes are usually a black-box hiding most of its numerical methods to the student.The python based open code {\bf SolidsPy} has been written to be used mainly as a teaching tool since it is structured in such a way that its independent subroutines can be used to show the student the different steps in the  construction of a finite element algorithm. Furthermore, the code is open and can be modified to satisfy the needs of a learning process without any risk as it can be restored to its original version.

This document describes the steps required for the generation of a finite element model for elastic two-dimensional media to be analyzed with {\bf Solidspy\_DYN}. It walks the student/user through the complete modeling process starting with the definition of the domain using external free open software, continuing with the writing of input files for the code and finishing with solution visualization with additional external free open software.

The first part of the document describes how to define input data files to {\bf Solidspy\_DYN} in terms of simple text files. The next section shows the construction of complex geometric domains using {\bf gmsh}, the external and free mesh generation program. Then the manual describes the process of writing the input files using yet another external software corresponding in this case to the python based module {\bf meshio}. The final part of the document describes the postprocessing or visualization with {\bf Paraview}.

In order to show the modeling process we use a simple problem and its {\bf gmsh} definition in terms of geometrical and physical entities as required to conduct dynamic analysis with {\bf Solidspy\_DYN}. The model to be analyzed is geometrically simple but it involves all the parts and steps required for the analysis. One of the main purposes in a dynamic analysis of continuum media is to capture the time history of the response of a given quantity. By default {\bf Solidspy\_DYN} writes the response time history in terms of displacements at every time step. The solution is written in the form of {\bf vtk} files ready to b visualized with {\bf Paraview}. This tutorial assumes that the user is familiar with the fundamental aspects of {\bf gmsh} and thus it is more focused in the parts required in the model so it can be analyzed with {\bf Solidspy\_DYN}.

To be able to follow this tutorial the user requires the following software:

\begin{itemize}
\item {\bf Solidspy\_DYN}: The dynamic version of the finite element analysis code {\bf Solidspy}.
\item {\bf Solidspy}: This basic code is required since we will used some of the libraries available in {\bf Solidspy}.
\item {\bf Gmsh}: The free mesh creation software.
\item {\bf meshio}: The input-output module to read and write the {\bf Gmsh} models.
\item {\bf paraview}: The free solutions visualization software.
\end{itemize}

\section{Creating a model in SolidsPy\_DYN }
In general, a finite element model for the solution of the theory of elasticity boundary value problem (BVP) involves (i) a discretization or partition of the problem domain into sub-domains, also known as the mesh, (ii) a description of the boundary conditions and (iii) a description of the material properties. Dynamic problems also require the definition of time parameters like initial conditions and a description o the time behavior of loads.

A finite element mesh is nothing else than a discrete set of points (or nodes) arranged into so-called finite elements or subdomains. The method itself, is based upon the local solution of the BVP in each element within which the displacement field at an arbitrary point is expressed in terms of the displacements at the nodal points. Once the displacement field is known it can be used in the determination of strains and stresses. The boundary conditions correspond to prescribed values of the displacements or loads at a given set of nodal points. Similarly, the material properties are just the values of the material parameters associated to the different materials present in the model.

In {\bf SolidsPy\_DYN} a finite element model is defined in terms of text files containing the nodal data (nodes.txt), the element data (eles.txt), the loading data (loads.txt), the material data (mater.txt) and in dynamic problems the time integration data (inipar.txt). The data structure of these text files will be described later.

There are several issues in the construction of a finite element model. First, if the domain has a complex geometry its partition into sub-domains becomes difficult. Second, if the corresponding mesh is moderately fine the number of nodes and elements results into large nodal and elemental text files. Here the problem of discretizing the arbitrary complex geometry into finite elements is solved using the free and open 3D finite element mesh generator {\bf Gmsh}. Once the geometry is discretized with {\bf Gmsh} the resulting files need to be converted into {\bf SolidsPy\_DYN} text files. This is achieved using {\bf meshio}\footnote{\url{<https://github.com/nschloe/meshio>}}, an input-output python based module for various mesh formats, including {\bf Gmsh}. The complete model creation process is schematized in \cref{fig:modeling}. The generation of the {\bf SolidsPy\_DYN} text files using meshio is completed using a problem dependent Python script written using subrotuines available in the preprocesor module in {\bf SolidsPy\_DYN}. The generation of the preprocessing script is described in \hyperref[meshio]{section \ref*{meshio} }.

\begin{figure}[H]
  \centering
  \includegraphics[width=10 cm]{img/modeling.pdf}
  \caption{Creation of a SolidsPy model. The geometry is built and discretized with Gmsh and SolidsPy text files are later written out by meshio.py.}
  \label{fig:modeling}
\end{figure}

In what follows we make a description of each one of the 5 {\bf SolidsPy\_DYN} text files.

\subsection{Elements data}
Each {\bf SolidsPy\_DYN} finite element model requires a text file (eles.txt) with the following information for each element in the mesh:
\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{elesT.txt}

where

\begin{itemize}
\item {\bf ele\_id}: Integer defining the id for the current element. The elements in the text file must be listed in consecutive ascending order.
\item {\bf ele\_tag}: Integer defining the type of element according to (1): 4-noded bi-lineal element, (2): 6-noded quadratic triangle, (3): 3-noded linear triangle.
\item {\bf mat\_tag }: An integer number defining the material profile associated to the current element. A material profile is a st of material parameters defining the elastic properties of a given material. These are defined in the mater.txt file.
\item{\bf  n1  n2  ...nN}: For a N-noded element a list of N nodal numbers defining the nodes of the current element.
\end{itemize}

The eles.txt file can be created out of a Gmsh file by the python subroutine {\bf ele\_writer()} available in the {\bf preprocesor} module of the program.

\subsection{Nodal data}
Each {\bf SolidsPy\_DYN} finite element model requires a text file (nodes.txt) with the following information for each nodal point in the mesh

\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{nodesT.txt}

where
\begin{itemize}
\item {\bf id}: Integer defining the id for the current node. The nodes in the text file must be listed in consecutive ascending order.
\item {\bf x-coord}: Horizontal coordinate of the nodal point in a cartesian reference system.
\item {\bf y-coord}: Vertical coordinate of the nodal point in a cartesian reference system.
\item{\bf  BC-x}: Horizontal displacement boundary condition flag at the current node. A value of -1 implies that the degree of freedom is restrained and a value of 0 that it is free.
\item{\bf  BC-y}: Vertical displacement boundary condition flag at the current node. A value of -1 implies that the degree of freedom is restrained and a value of 0 that it is free.
\end{itemize}

The nodes.txt file can be created out of a Gmsh file by the python subroutine {\bf node\_writer()} available in the {\bf preprocesor} module of the program. Since the displacement boundary conditions are different than 0 only at a limited set of nodes these can be written using the python subroutine {\bf boundary\_conditions()} available in the {\bf preprocesor} module of the program.

\subsection{Materials data}
Each {\bf SolidsPy\_DYN} finite element model requires a text file (mater.txt) with the following information for each material profile in the mesh. A material profile is a set of parameters corresponding to a given material. Each material profile is defined by a line of text with the following parameters:

\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{materT.txt}

where
\begin{itemize}
\item $E$      : Young modulus
\item $\nu$   : Poisson's ratio.
\item $\rho$  : Mass density.
\item $D_1$  : Constant for mass proportional damping.
\item $D_2$  :Constant for stiffness proportional damping.
\end{itemize}

The mater.txt file must be manually created by the user.

\subsection{Loading data}
Each {\bf SolidsPy\_DYN} finite element model requires a text file (loads.txt) with the following information for each loaded node in the mesh.
\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{loadsT.txt}

where

\begin{itemize}

\item {\bf Node}: Node number for the current load.
\item {\bf $F_x$}: Magnitude of the nodal point load in the horizontal direction.
\item {\bf $F_y$}: Magnitude of the nodal point load in the vertical direction.

\end{itemize}

{\bf SolidsPy\_DYN} only considers nodal point loads, and thus surface tractions must be converted into nodal point loads. This conversion can be made using the Gmsh file and the python subroutine {\bf loading()} available in the {\bf preprocesor} module of the program.

\subsubsection{Time dependence of the loading data}

All the loads in {\bf Solidspy\_DYN} are specified with a time variation in terms of a Ricker pulse. This time function is defined as follows:

 \[A = \left( {1 - 2{\pi ^2}f_c^2{t^2}} \right){e^{ - {\pi ^2}f_c^2{t^2}}}\]

 where $f_c$ is the central frequency. In {\bf Solidspy\_DYN} the pulse is centered as specified by $t_c$.

 \begin{figure}[H]
  \centering
  \includegraphics[width=6 cm]{img/pulse.pdf}
  \includegraphics[width=6 cm]{img/fourier.pdf}
    \caption{Time function and Fourier spectra for the Ricker pulse. The signal is characterized by its central time $t_c$ and its dominant frequency parameter $f_c$}
  \label{fig:mesh}
\end{figure}

The load is defined using data combined in two files. In the loads.txt file the user must specify a list of nodes with the amplitude of the load along each degree of freedom. The remaining parameters corresponding to the time variation of the load and completely defining the Ricker pulse are indicated in the integration parameters file of the model as described next.

\subsection{Time dependent data}
Each {\bf SolidsPy\_DYN} finite element model requires a text file (inipar.txt) with the following time dependent information for the analysis:

\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{iniparT.txt}

where

\begin{itemize}
\item {\bf $dt$}   : Time step for the incremental analysis.
\item {\bf $T$}    : Duration of the analysis.
\item {\bf $t_c$} : Central time in the Ricker pulse.
\item {\bf $f_c$} : Central frequency in the Ricker pulse.
\end{itemize}

\section{Definition of a finite element model for SolidsPy\_DYN using Gmsh}

This section describes the basic components and steps for the construction of a finite element model to conduct dynamic analysis with {\bf Solidspy\_DYN}. It is assumed that the user is familiar with Gmsh. There are several tutorials in the TUTORIALS folder of the code.

The model construction process is explained with reference to the problem shown in \cref{fig:placa}  corresponds to a rectangular plate composed of two different materials  and submitted to a point load with time variation defined by a Ricker pulse applied at the top.

\begin{figure}[h]
  \centering
  \includegraphics[width=8 cm]{img/placa.pdf}
  \caption{Rectangular plate conformed by two different materials under a time varying point load at the free surface.}
  \label{fig:placa}
\end{figure}

The construction of a model in {\bf Gmsh} involves the generation of geometrical elements defining all the points, lines and planes required to build the geometry and physical groups corresponding to special sets of points, lines and planes (or surfaces) where particular modeling conditions are expected to occur. The names or labels assigned to the physical groups are later associated to the specific elements of the mesh pertaining to these groups.

For instance if nodal points are to be applied over a given line of the model, it is convenient to define that specific line as physical line. Later during the mesh creation process all the nodes lying along that specific physical line will be stored in an independent data structure facilitating the modeling process. The following sections describe the generation of the geometrical and physical groups required in a {\bf Gmsh} model for {\bf SolidsPy\_DYN}\footnote{The following link contains a series of videos showing the modeling process for this example \url{<https://www.youtube.com/channel/UCNhX9Z5wkEk_JhlSuIo8A4Q>}}.

\subsection{Geometrical entities}
The geometrical entities of the model are shown in \cref{fig:geometrical} together with the conventions used to describe each entity. The corresponding entities or groups are in hierarchical order points, lines (formed by points), line loops (formed by groups of lines generating a closed figure) and plane surfaces (formed by a line loop). The current model contains two plane surfaces since we will be using two different materials in each part of the domain. When creating the model it is convenient to use parameters for all the dimensions as this will facilitate the execution of several analysis later. Here we will assume that the model is of width $L$, while the lateral faces are specified by dimensions $H1$ and $H2$. As additional parameter we will use the characteristic element size $hc$ to be specified as a fraction of the total width.

\begin{figure}[h]
  \centering
  \includegraphics[width=8 cm]{img/geometrical.pdf}
  \caption{Geometrical entities for the model.}
  \label{fig:geometrical}
\end{figure}

\subsection{Physical entities}
The physical groups in the model are described in \cref{fig:physical}. These correspond to the two physical surfaces labeled (100) and (200) respectively; a physical line, labeled (400) and a physical point with the label (500). These names will be assigned to all the nodes and elements that coincide with those specific groups. The physical surfaces are required in order to indicate which elements are assigned each material profile. The physical line is required in order to identify the nodes at which displacement boundary conditions are to be imposed and the physical node is required in order to specify the loading node. Each physical group is described next:

\begin{itemize}
\item Physical surface $100$: Top material layer.
\item Physical surface $200$: Bottom material layer.
\item Physical line $400$: Bottom surface with all restrained vertical and horizontal displacements.
\item Physical point $500$: Localization of the dynamic point load.
\end{itemize}

\begin{figure}[h]
  \centering
  \includegraphics[width=8 cm]{img/physical.pdf}
  \caption{Physical entities for the model.}
  \label{fig:physical}
\end{figure}

The model defined in terms of geometrical and physical groups is returned by {\Gmsh} as a file with extension \texttt{.geo}. The specific \texttt{.geo} file for the example problem is shown next.

\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{bimaterial.geo}

After conducting a couple of mesh refinements the discretized model should look as shown in \cref{fig:mesh}

\begin{figure}[H]
  \centering
  \includegraphics[width=8 cm]{img/mesh.pdf}
  \caption{Finite element mesh conformed of linear triangular elements.}
  \label{fig:mesh}
\end{figure}

\section{Generation of text files from the Gmsh model}
\label{meshio}
The input data in terms of text files for the finite element code must now be defined using the {\bf gmsh} model. This is achieved using subroutines available in the {\bf preprocessor} module of {\bf SolidsPy}. These routines are written on top of the input-output python based module {\bf meshio}. These subroutines are essentially required to write the nodes.txt and elements.txt files required by {\bf Solidspy\_DYN} which may result massive and cumbersome to process. The python script that produces the input files for the current example is described next.

\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{bimaterial.py}

In the first block we import the required modules for the script. In particular the script requires the module {\bf meshio} and the module {\bf preprocesor} from {\bf solidspy}.

After the modules have been imported, the first line of code uses {\bf meshio} to read the {\bf gmsh} mesh. In this case the mesh has been saved with the name {\bf bimaterial.msh}. Once the mesh is read all the model is stored in python dictionaries ready to be processed. The process of manipulating the dictionaries and extracting the data for the text files is done with subroutines available in the module  {\bf preprocesor} from {\bf solidspy} and this implies that for these routines to be available {\bf solidspy} has been previously installed.

In the first part the points and point\_data dictionaries are used by the subroutine node\_writer to write an array with the nodal point data in the format required by the finite element code.

The element processing is performed by the element writer subroutine. This subroutine must be invoked as many times as there are physical surfaces in the model. The subroutine uses the following input parameters:

\begin{itemize}
\item  The python dictionaries cells and cell\_data storing all the element information.
\item  The name for the element used in the current surface according to {\bf gmsh} which in this case corresponds to "triangle" for the lineal triangles.
\item The number of the surface being processed according to {\bf gmsh}. In this case we have defined physical surfaces with numbers $100$ and $200$ for the top and bottom surface respectively.
\item A number identifying the type of element according to {\bf Solidspy\_DYN} which in this case is $3$ since we are using a triangular lineal element.
\item  A number identifying the material profile to be used in the current surface. In this case the top surface has been assigned the material properties defined in the material profile $0$ while the bottom surface has been assigned those from material profile $1$.
\item An integer parameter specifying where the current surface should start counting elements. In this particular problem the element writer starts counting elements from $0$ in the first surface and from {\bf nini} in the second surface where {\bf nini} is the last element from the previous surface.
\end{itemize}

Upon execution this subroutine returns an array with the elemental information for the current surface but now written in {\bf Solidspy\_DYN} format and an integer number indicating the total number of elements contained in that surface. This number is required if processing several surfaces since this marks down where the next element counting should begin.

The next part of the script specifies the boundary conditions. Thus boundary conditions subroutine requires the following input parameters:

\begin{itemize}
\item The python dictionaries cells and cell\_data storing all the element information.
\item An integer specifying the name assigned to the line where the current boundary condition is to be imposed. In this case we have used the number $400$ to specify the bottom line of the model.
\item The array containing the nodal data and generated by the nodes writer subroutine. This array will be modified by the boundary conditions subroutine.
\item Two boundary condition specifiers indicating if the horizontal and vertical degrees of freedom are free (value $=0$) or restrained (value $=-1$).
\end{itemize}

The final part of the script uses the previously generated arrays and writes the corresponding text files. Since we have processed two physical surfaces the two resulting arrays must be concatenated into a single element array ready for output. This is done using the python function append.

\section{Visualization of displacement time histories}
\subsection*{Animated view of the propagation patterns}
In wave propagation analysis it is common to develop conceptual understanding of a given solution based upon the propagation pattern experienced by the propagated wave field. In {\bf Solidspy\_DYN} these visualizations can be realized  using the external open-free visualization software {\bf Paraview}. By default {\bf Solidspy\_DYN} writes ready-to-process VTK files that can be read by {\bf Paraview}. Each output VTK file corresponds to a snapshot of the solution at given time instant.  These files are stored by {\bf Solidspy\_DYN} in the folder /MESHES/VTKs.

\subsection*{View of the surface displacements at a line of points}
A second possibility for the visualization of results is to simultaneously plot the time response for a series of points located along a given line. If the resulting time signals are plotted with the amplitudes progressively scaled by a constant factor the plot gives a physical sense of the propagation pattern.

The generation of results for this type of visualization requires the creation of an additional file, {\bf salida.txt}, storing the identification numbers for the set of response points. This file is created directly from the same script that generates the text files for the model after adding the function {\bf respuesta ()}. This function extracts the nodal identifiers located along a previously defined physical line and writes the required text file.

The second step required to obtain the sheet-like visualization of the response is to execute the analysis within a particular script. The script executes the following steps:

\begin{itemize}
\item Executes the code and computes the time history of displacements at all the nodes. This solution is stored in the array {\bf U}.
\item Loads the text file {\bf salida.txt} into the array {\bf salida}.
\item Invokes the post-processing function {\bf sheets} using as input parameters the array {\bf salida} and the solution array {\bf U}. This function writes down the response into a second file named {\bf respuesta.txt}.
\item Load the file {\bf respuesta.txt} into the array {\bf vals}.
\item Use either of/or both functions {\bf plot_sheet} and {\bf plot_pcolor} using as input parameter the array {\bf vals} to plot the solution.
\end{itemize}

An example of the sheet-like visualization of the results is shown in \cref{fig:sabanas}, while the specific function {\bf respuesta ()} that generates the file for output and the script for execution of the code in post-processing mode are included at the end.

\begin{figure}[H]
  \centering
  \includegraphics[width=8 cm]{img/sabana1.pdf}\\
    \includegraphics[width=8 cm]{img/sabana2.pdf}
  \caption{Visualization of response time histories at a set of nodal points located along the same line.}
  \label{fig:sabanas}
\end{figure}

\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{generador.py}

\inputminted[mathescape,
               numbersep=5pt,
               gobble=0,
               frame=lines,
               framesep=2mm]{c}{ejecucion_postpr.py}

\bibliographystyle{gji}
\bibliography{bibliografia}

\end{document}


\documentclass[12pt,a4paper,twoside]{article}

\usepackage[a4paper,left=2.2cm,right=2.2cm,bottom=2.5cm,top=2.5cm,footskip=32pt]{geometry}
\usepackage{setspace}
\usepackage{time}
\usepackage[sort]{natbib}
\usepackage{graphicx}
\usepackage{amsmath,amssymb,bm}
% \usepackage[dvipsnames]{xcolor}
\usepackage[table,xcdraw,dvipsnames]{xcolor}
\usepackage{hyperref}
\usepackage{multirow}
\usepackage{cleveref}

\usepackage{epigraph}
\setlength{\epigraphwidth}{2.9in}
\usepackage{authblk}
\newsavebox\CBox
\def\textBF#1{\sbox\CBox{#1}\resizebox{\wd\CBox}{\ht\CBox}{\textbf{#1}}}

\newcommand\myshade{85}
\colorlet{mylinkcolor}{violet}
\colorlet{mycitecolor}{YellowOrange}
\colorlet{myurlcolor}{Aquamarine}

\hypersetup{
	linkcolor  = mylinkcolor!\myshade!black,
	citecolor  = mycitecolor!\myshade!black,
	urlcolor   = myurlcolor!\myshade!black,
	colorlinks = true,
}

\usepackage{fancyhdr}
\usepackage[printwatermark]{xwatermark}
%\newwatermark[allpages,color=gray!50,angle=45,scale=1.5,xpos=0,ypos=0]{Draft. Do not circulate or cite}

\begin{document}%Data and Methods

\title{\normalsize{Extended abstract submitted to the International Population Conference 2021}\\
	\normalsize{\emph{Hyderabad, 5-10 December 2021}}\\
	$\,$\\	$\,$\\
	\begin{Huge}Forecasting vital rates from \\demographic summary measures\end{Huge}}

	\author[1]{Carlo G.~Camarda\thanks{Corresponding author: \url{carlo-giovanni.camarda@ined.fr}\\
		\hspace*{1.8em}Address: 9 cours des Humanités, 93322 Aubervilliers - France}}
\author[2]{Jos\'e Manuel Aburto}
\affil[1]{\small \textit{Institut national d'\'{e}tudes d\'emographiques (INED)}}
\affil[2]{\small \textit{Department of Sociology and Leverhulme Centre for Demographic Science,} \authorcr \textit{University of Oxford}}

\date{}
\maketitle
\thispagestyle{empty}

\pagestyle{fancy}
\fancyhead[RO,LE]{\small}
\fancyhead[LO]{\small Camarda \& Aburto: Forecasting vital rates from summary measures}% odd page header and number to right top
\fancyhead[RE]{\small Submitted to the IPC 2021}%Even page header and number at left top
\fancyfoot[L,R,C]{}
\rfoot{\thepage}

	\begin{abstract}
In population and actuarial sciences, time-trends of summary measures (such as life expectancy or the average number of children per woman) are easy to interpret and predict. Most summary measures are nonlinear functions of the vital rates, the key variable we usually want to estimate and forecast. Furthermore smooth outcomes of future age-specific vital rates are desirable. Therefore, optimization with nonlinear constraints in a smoothing setting is necessary. We propose a methodology that combines Sequential Quadratic Programming and a $P$-spline approach, allowing to forecast age-specific vital rates when future values of demographic summary measures are provided. We provide an application of the model on Italian mortality and Spanish fertility data.
\\
\\$\,$\\
{\bf Keywords:} Vital rates forecast; Smoothing; Constrained nonlinear optimization; Summary measures.
	\end{abstract}

%	\keywords{}

	%%%%%--------------------------------------%%%%%%%%%%%%%%%%%%%
	%%%%%--------------------------------------%%%%%%%%%%%%%%%%%%%

	\newpage
	\pagenumbering{arabic}
\setcounter{page}{1}
\section{Introduction}\label{sec:Intro}

Future mortality and fertility levels can be predicted either by modelling and extrapolating rates over age and time, or by forecasting summary measures, later converted into age-specific rates.
The latter approach takes advantage of the prior knowledge that demographers and actuaries have on possible future values of measures such as life expectancy at birth and total fertility rate. Among others, this methodology has been lately adopted by the United Nations \citep{SecikovaEtAlRates2016}.
In this study, we propose a model to derive future mortality and fertility age-patterns complying with  projected summary measures. Unlike comparable approaches, we assume only smoothness of future vital rates, which is achieved by a two-dimensional $P$-spline approach as in \citep{CurrieDurbanEilersSmoothForecast2004}, and we allow constraints to multiple series of summary measures. Since these measures are commonly nonlinear functions of the estimated penalized coefficients, Lagrangian multipliers cannot be directly implemented. We hence opted for a Sequential Quadratic Programming (SQP) procedure \citep{NocedalWright2006} to perform the associated constrained nonlinear optimization.
We illustrate our approach with two data sets. We forecast mortality of Italian females, based on future life expectancy predicted by \citet{WPP2019} and a future trend of a lifespan disparity measure obtained by time-series analysis. We also forecast Spanish fertility constrained to future values of total fertility rates, mean and variance of age at childbearing, derived by time-series analysis.

\section{Model on Italian mortality data}

For ease of presentation, we formulate the model on mortality data. We suppose that we have deaths, and exposures to risk, arranged in two matrices,
$\bm{Y} = (y_{ij})$ and $\bm{E} = (e_{ij})$, each $m \times n_{1}$, whose rows and columns are classified by age at death, $\bm{a}, \,m \times 1$, and year of death, $\bm{t}_{1}, \,n_{1} \times 1$, respectively.
We assume that the number of deaths $y_{ij}$ at age $i$ in year $j$ is Poisson distributed with mean $\mu_{ij} \,e_{ij}$. %The value of $\mu_{ij}$ is commonly named force of mortality.
Forecasting aims to reconstruct trends in $\mu_{ij}$ for $n_{2}$ future years, $\bm{y}_{2}, n_{2} \times 1$.

It is common practice to summarize mortality age-patterns by computing measures such as life expectancy at birth ($e_{0}$) and lifespan disparity measures. Time-trends of these summary measures are often regular and well-understood. Forecasting these time-series is therefore an easier task. Figure~\ref{fig:CamardaMort} (top-left panel) presents observed $e_{0}$ for Italian females from 1960 to 2016 along with the medium variant up to 2050 as computed by the UN. A second constraint is given by future values of $e^{\dagger}$, a lifespan disparity measure defined as the average years of life lost in a population attributable to death \citep{VaupelCRdemo2003}. Future values of this measure are obtained by conventional time-series models and portrayed in the top-right panel of Figure~\ref{fig:CamardaMort}. Future mortality patterns, both by age and over time, must adhere to these predicted trends.

\begin{figure}[!ht]\centering
	\includegraphics[scale=0.35]{./Figures/Camardafig1a.pdf}
	\includegraphics[scale=0.35]{./Figures/Camardafig1b.pdf}
	\includegraphics[scale=0.35]{./Figures/Camardafig1c.pdf}
	\includegraphics[scale=0.35]{./Figures/Camardafig1d.pdf}
	\caption{\label{fig:CamardaMort} Top panels: Actual, estimated and forecast life expectancy at birth and lifespan disparity measure by United Nations and time-series, 2D $P$-splines and the SQP+2D $P$-splines. Bottom panels: Mortality in 2050 described by log-hazards and associated densities (ages 50+) by 2D $P$-splines and the SQP+2D $P$-splines. Italian females, ages 0-105, years 1960-2014, forecast up to 2050.}
\end{figure}

We arrange data as a column vector, that is, $\bm{y} = \verb"vec"(\bm{Y})$ and $\bm{e} = \verb"vec"(\bm{E})$ and we model our Poisson death counts as follows: $\ln(E(\bm{y})) = \ln(\bm{e})+ \bm{\eta} = \ln(\bm{e})+ \bm{B}\,\bm{\alpha}\, , $ where $\bm{B}$ is the regression matrix over the two dimensions: $\bm{B} = \bm{I}_{n_{1}} \otimes \bm{B}_{a}$, with $\bm{B}_{a} \in \mathbb{R}^{m \times k_{a}}$. Over time, we employ an identity matrix of dimension $n_{1}$ because we will incorporate a constraint for each year. Over age, $\bm{B}_{a}$ includes a specialized coefficient for dealing with mortality at age 0. In order to forecast, data and bases are augmented as follows:
\begin{equation}\label{eq:AugData}
\breve{\bm{E}} = [\bm{E} : \bm{E}_{2}]\, , \qquad
\breve{\bm{Y}} = [\bm{Y} : \bm{Y}_{2}]\, , \qquad
\breve{\bm{B}} = \bm{I}_{n_{1}+n_{2}} \otimes \bm{B}_{a}
\, ,
\end{equation}
where $\bm{E}_{2}$ and $\bm{Y}_{2}$ are filled with arbitrary future values. If we define a weight matrix $\bm{V} = \mathrm{diag}(\verb"vec"(\bm{1}_{m\times n_{1}}:\bm{0}_{m\times n_{2}}))\,$, the coefficients vector $\bm{\alpha}$
can be estimated by a penalised version of the iteratively reweighted least squares algorithm:
\begin{equation}\label{eq:penIRWLSfor}
(\breve{\bm{B}}' \bm{V} \tilde{\bm{W}} \breve{\bm{B}} + \bm{P}) \tilde{\bm{\alpha}} =
\breve{\bm{B}}'\bm{V} \tilde{\bm{W}}\tilde{\bm{z}} \, ,
\end{equation}
where a difference penalty $\bm{P}$ enforces smoothness behaviour of mortality both over age and time. Outcomes from this approach in terms of life expectancy and $e^{\dagger}$ are depicted with a dashed line in Figure~\ref{fig:CamardaMort} (top panels), and departures from the UN and time-series projected values are evident.

Both life expectancy and average years of life lost are nonlinear function of the coefficients vector $\bm{\alpha}$. For a year $j$ and associated $k_{a}$ coefficients $\bm{\alpha}_{j}$, we denote mortality by $\bm{\mu}_{j} = \exp(\bm{B}_{a}\bm{\alpha}_{j})$. We can write our summary measures as follows
\begin{eqnarray}\label{eq:e0ed}
e^{0} (\bm{\alpha}_{j}) &=& \;\;\,\,\bm{1}_{m}' \, \exp[ \bm{C} \, \bm{\mu}_{j}]  + 0.5 \\
e^{\dagger} (\bm{\alpha}_{j}) &=&  - \exp[ \bm{C} \, \bm{\mu}_{j}]' \, \bm{C} \bm{\mu}_{j} \nonumber
\end{eqnarray}
where $\bm{C}$ is a $(m \times m)$ lower triangular matrix filled only with -1.

%\begin{equation}\label{eq:Cmat}
%\bm{C} = \left[\begin{array}{rrrr}
%-1 & 0 & \cdots & 0 \\
%-1 & -1& \cdots & 0 \\
%\vdots & \vdots & \ddots & \vdots \\
%-1 & -1 & \cdots & -1
%\end{array}\right] \, .
%\end{equation}

Constrained nonlinear optimization is therefore	necessary and a SQP approach is implemented. Let denote with $\bm{N}^{0}$ and $\bm{N}^{\dagger}$ the $(k_{a}n_{2} \times n_{2})$ matrices with block-diagonal structures containing derivatives of~\eqref{eq:e0ed} with respect to $\bm{\alpha}_{j}$ for $j=n_{1}+1, \ldots n_{1}+n_{2}$:
\begin{eqnarray}\label{eq:Der}
\frac{\partial e^{0} (\bm{\alpha}_{j})}{\partial \bm{\alpha}_{j}} &=& \bm{1}_{m}' \verb"diag"[\exp(\bm{C}\bm{\mu}_{j})] \,	\bm{C} \,\verb"diag"(\bm{\mu}_{j}) \bm{B}_{a}\\
\frac{\partial e^{\dagger} (\bm{\alpha}_{j})}{\partial \bm{\alpha}_{j}} &=& - \bm{B}_{a}' \left\{ \bm{C}'[\bm{C}\bm{\mu}_{j} \circ \exp(\bm{C}\bm{\mu}_{j})] \circ \bm{\mu}_{j} \right\} + \nonumber\\
&& - \bm{B}_{a}' \left\{ [\bm{C}' \exp(\bm{C}\bm{\mu}_{j})] \circ \bm{\mu}_{j} \right\} \nonumber\, ,
\end{eqnarray}
where $\circ$ represents element-wise multiplication. Target life expectancy and lifespan disparity for future years are given by $n_{2}$-vectors $\bm{e}^{0}_{\mathrm{T}}$ and $\bm{e}^{\dagger}_{\mathrm{T}}$.

Solution of the associated system of equations at the step $\nu + 1$ is given by
\begin{equation}\label{eq:SQLalg}
\left[ \begin{array}{l}
\bm{\alpha}_{\nu+1}\\
\bm{\omega}_{\nu+1}
\end{array}\right] =
\left[ \begin{array}{lllll}
\bm{L}_{\nu}  &:& \bm{H}^{0}_{\nu} &:& \bm{H}^{\dagger}_{\nu}\\
\bm{H}_{\nu}^{0 T}  &:& \bm{0}_{n_{2} \times n_{2}}&:&\bm{0}_{n_{2} \times n_{2}}\\
\bm{H}_{\nu}^{\dagger T} &:& \bm{0}_{n_{2} \times n_{2}} &:& \bm{0}_{n_{2} \times n_{2}}
\end{array}\right]^{-1}
\left[ \begin{array}{c}
\bm{r}_{\nu} - \bm{L}_{\nu}\bm{\alpha}_{\nu}\\
\bm{e}^{0}_{\mathrm{T}} - \bm{e}^{0} (\bm{\alpha}_{\nu})\\
\bm{e}^{\dagger}_{\mathrm{T}} - \bm{e}^{\dagger} (\bm{\alpha}_{\nu})\\
\end{array}\right] \, ,
\end{equation}
where $\bm{L}$ and $\bm{r}$ are left- and right-hand-side of the system in~\eqref{eq:penIRWLSfor}, and matrices $\bm{H}^{0} = \left[\bm{0}_{k_{a}n_{1}\times n_{2}}:\bm{N}^{0} \right]'$ and $\bm{H}^{\dagger} = \left[\bm{0}_{k_{a}n_{1}\times n_{2}}:\bm{N}^{\dagger} \right]'$. Vector of $\bm{\omega}$ denotes the current solution of the associated Lagrangian multipliers for both set of constraints.

Future values for $e^{0}$ and $e^{\dagger}$ forecast by the proposed method are exactly equal to the UN and time-series values (Figure~\ref{fig:CamardaMort}, top panels). The bottom panels show the forecast mortality age-pattern in 2050: the shape obtained by the suggested approach is not a simple linear function of the plain $P$-splines outcome, and differences are evident by looking at the associated age-at-death distributions.

\section{Spanish Fertility Data}

We forecast Spanish fertility using three commonly-used summary measures: Total Fertility Rate describing average number of children per women in a given year, and mean and variance of childbearing age which measure fertility shape over age. In formulas:
\begin{eqnarray}\label{eq:FertMea}
TFR(\bm{\alpha}_{j}) &=& \bm{1}_{m}' \, \bm{\mu}_{j}\\
MAB(\bm{\alpha}_{j}) &=& \bm{\mu}_{j}' \, (\bm{a}+0.5) \; / \; TFR(\bm{\alpha}_{j})\nonumber\\
VAB(\bm{\alpha}_{j}) &=& \bm{\mu}_{j}' \, (\bm{a}+0.5)^2 \; / \; TFR(\bm{\alpha}_{j}) - MAB(\bm{\alpha}_{j})^2\nonumber \, .
\end{eqnarray}

We forecast trends of these measures by time-series analysis. We then smooth and constrain future fertility age-patterns to comply forecast values of \eqref{eq:FertMea} as in \eqref{eq:SQLalg}. Summary measures as well as fertility rates in 2050 are presented in Figure~\ref{fig:CamardaFert}. Differences between proposed approach and plain 2D $P$-splines are clear. Whereas $P$-splines blindly extrapolate previous trends mainly accounting for the last observed years, the proposed approach enforces future age-patterns to adhere combinations of summary measures, guiding future fertility toward demographic meaningful trends.

\begin{figure}[!ht]\centering
	\includegraphics[scale=0.35]{./Figures/Camardafig2a.pdf}
	\includegraphics[scale=0.35]{./Figures/Camardafig2b.pdf}
	\includegraphics[scale=0.35]{./Figures/Camardafig2c.pdf}
	\includegraphics[scale=0.35]{./Figures/Camardafig2d.pdf}
	\caption{\label{fig:CamardaFert} Top and left-bottom panels: Actual, estimated and forecast Total Fertility Rate, Mean and Variance in childbearing age by time-series analysis, 2D $P$-splines and the SQP+2D $P$-splines. Right-bottom panel: Age-specific fertility rate in 2050 by 2D $P$-splines and the SQP+2D $P$-splines. Spain, ages 12-55, years 1960-2016, forecast up to 2050.}
\end{figure}

\section{Concluding remarks}

In this study, we combine smoothing models ($P$-splines) and optimization with nonlinear constraints (Sequential Quadratic Programming) to forecast vital rates when future values of demographic summary measures are provided.
%The proposed approach allows to obtain smooth future mortality and fertility patterns which comply projected measures commonly easy to interpret and predict.

We envisage further applications. Forecast of vital rates for partially completed cohorts is often relevant in population studies. For instance, final fertility history of a given cohort may be hypothesized though age-pattern is not yet observed and its estimation will be necessary. We also plan to adopt our approach to reconstruct demographic scenarios which are conventionally based on summary measures.

From a methodological perspective, future work will be realized to incorporate uncertainty and to objectively select the amount of smoothness in future mortality and fertility age-patterns.

\newpage
\thispagestyle{empty}
\bibliographystyle{chicago}
\bibliography{bibliografia1}

\end{document}