2019-06-24 17:52:09 +02:00
/*
2023-01-23 16:21:07 +01:00
* Copyright © 2019 - 2023 Dynare Team
2019-06-24 17:52:09 +02:00
*
* This file is part of Dynare .
*
* Dynare is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* Dynare is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2021-06-09 17:33:48 +02:00
* along with Dynare . If not , see < https : //www.gnu.org/licenses/>.
2019-06-24 17:52:09 +02:00
*/
# include <string>
# include <memory>
# include <limits>
2023-01-23 16:21:07 +01:00
# include <type_traits>
# include <algorithm>
2019-06-24 17:52:09 +02:00
2019-06-25 11:13:48 +02:00
# if defined(_WIN32) || defined(__CYGWIN32__)
# ifndef NOMINMAX
# define NOMINMAX // Do not define "min" and "max" macros
# endif
# include <windows.h>
# else
# include <dlfcn.h> // unix/linux DLL (.so) handling routines
# endif
2019-06-24 17:52:09 +02:00
class DynamicModelCaller
{
public :
2019-11-14 14:39:28 +01:00
const bool linear ;
bool compute_jacobian ; // Not constant, because will be changed from true to false for linear models after first evaluation
2019-06-24 17:52:09 +02:00
// Used to store error messages (as exceptions cannot cross the OpenMP boundary)
static std : : string error_msg ;
2019-12-20 14:50:19 +01:00
DynamicModelCaller ( bool linear_arg , bool compute_jacobian_arg ) : linear { linear_arg } , compute_jacobian { compute_jacobian_arg }
{
2023-01-23 16:21:07 +01:00
}
2019-06-24 17:52:09 +02:00
virtual ~ DynamicModelCaller ( ) = default ;
2023-01-23 16:21:07 +01:00
virtual double * y ( ) const = 0 ;
virtual double * x ( ) const = 0 ;
/* Copy a column of the Jacobian to dest.
Only copies non - zero elements , according to g1_sparse_ { rowval , colval , colptr } . */
virtual void copy_jacobian_column ( mwIndex col , double * dest ) const = 0 ;
virtual void eval ( double * resid ) = 0 ;
2019-06-24 17:52:09 +02:00
} ;
class DynamicModelDllCaller : public DynamicModelCaller
{
private :
2019-06-25 11:13:48 +02:00
# if defined(_WIN32) || defined(__CYGWIN32__)
2023-01-23 16:21:07 +01:00
static HINSTANCE resid_mex , g1_mex ;
2019-06-25 11:13:48 +02:00
# else
2023-01-23 16:21:07 +01:00
static void * resid_mex , * g1_mex ;
2019-06-25 11:13:48 +02:00
# endif
2023-01-23 16:21:07 +01:00
using dynamic_tt_fct = void ( * ) ( const double * y , const double * x , const double * params , const double * steady_state , double * T ) ;
using dynamic_fct = void ( * ) ( const double * y , const double * x , const double * params , const double * steady_state , const double * T , double * value ) ;
2019-06-24 17:52:09 +02:00
static dynamic_tt_fct residual_tt_fct , g1_tt_fct ;
2023-01-23 16:21:07 +01:00
static dynamic_fct residual_fct , g1_fct ;
const double * params , * steady_state ;
std : : unique_ptr < double [ ] > tt , y_p , x_p , jacobian_p ;
const int32_T * g1_sparse_colptr ;
2019-06-24 17:52:09 +02:00
public :
2023-01-23 16:21:07 +01:00
DynamicModelDllCaller ( size_t ntt , mwIndex ny , mwIndex nx , const double * params_arg , const double * steady_state_arg , const int32_T * g1_sparse_colptr_arg , bool linear_arg , bool compute_jacobian_arg ) ;
2019-06-24 17:52:09 +02:00
virtual ~ DynamicModelDllCaller ( ) = default ;
2023-01-23 16:21:07 +01:00
double *
y ( ) const override
2019-12-20 14:50:19 +01:00
{
2023-01-23 16:21:07 +01:00
return y_p . get ( ) ;
}
double *
x ( ) const override
2019-12-20 14:50:19 +01:00
{
2023-01-23 16:21:07 +01:00
return x_p . get ( ) ;
}
void copy_jacobian_column ( mwIndex col , double * dest ) const override ;
void eval ( double * resid ) override ;
2019-06-24 17:52:09 +02:00
static void load_dll ( const std : : string & basename ) ;
static void unload_dll ( ) ;
} ;
class DynamicModelMatlabCaller : public DynamicModelCaller
{
private :
std : : string basename ;
2023-01-23 16:21:07 +01:00
mxArray * y_mx , * x_mx , * jacobian_mx , * params_mx , * steady_state_mx , * g1_sparse_rowval_mx , * g1_sparse_colval_mx , * g1_sparse_colptr_mx ;
/* Given a complex matrix (of double floats), returns a sparse matrix of same size.
2019-06-24 17:52:09 +02:00
Real elements of the original matrix are copied as - is to the new one .
Complex elements are replaced by NaNs .
2023-01-23 16:21:07 +01:00
Destroys the original matrix .
There are two versions , one for dense matrices , another for sparse
matrices . */
template < bool sparse >
static mxArray * cmplxToReal ( mxArray * cmplx_mx ) ;
2019-06-24 17:52:09 +02:00
public :
2023-01-23 16:21:07 +01:00
DynamicModelMatlabCaller ( std : : string basename_arg , mwIndex ny , mwIndex nx , const mxArray * params_mx_arg , const mxArray * steady_state_mx_arg , const mxArray * g1_sparse_rowval_mx_arg , const mxArray * g1_sparse_colval_mx_arg , const mxArray * g1_sparse_colptr_mx_arg , bool linear_arg , bool compute_jacobian_arg ) ;
2019-06-24 17:52:09 +02:00
~ DynamicModelMatlabCaller ( ) override ;
2023-01-23 16:21:07 +01:00
double *
y ( ) const override
2019-12-20 14:50:19 +01:00
{
2023-01-23 16:21:07 +01:00
return mxGetPr ( y_mx ) ;
}
double *
x ( ) const override
2019-12-20 14:50:19 +01:00
{
2023-01-23 16:21:07 +01:00
return mxGetPr ( x_mx ) ;
}
void copy_jacobian_column ( mwIndex col , double * dest ) const override ;
void eval ( double * resid ) override ;
2019-12-20 14:50:19 +01:00
class Exception
{
2019-06-24 17:52:09 +02:00
public :
const std : : string msg ;
2019-12-20 14:50:19 +01:00
Exception ( std : : string msg_arg ) : msg { std : : move ( msg_arg ) }
{
2023-01-23 16:21:07 +01:00
}
2019-06-24 17:52:09 +02:00
} ;
} ;
2023-01-23 16:21:07 +01:00
template < bool sparse >
mxArray *
DynamicModelMatlabCaller : : cmplxToReal ( mxArray * cmplx_mx )
{
mxArray * real_mx { sparse ?
mxCreateSparse ( mxGetM ( cmplx_mx ) , mxGetN ( cmplx_mx ) , mxGetNzmax ( cmplx_mx ) , mxREAL ) :
mxCreateDoubleMatrix ( mxGetM ( cmplx_mx ) , mxGetN ( cmplx_mx ) , mxREAL ) } ;
if constexpr ( sparse )
{
std : : copy_n ( mxGetIr ( cmplx_mx ) , mxGetNzmax ( cmplx_mx ) , mxGetIr ( real_mx ) ) ;
std : : copy_n ( mxGetJc ( cmplx_mx ) , mxGetN ( cmplx_mx ) + 1 , mxGetJc ( real_mx ) ) ;
}
# if MX_HAS_INTERLEAVED_COMPLEX
mxComplexDouble * cmplx { mxGetComplexDoubles ( cmplx_mx ) } ;
# else
double * cmplx_real { mxGetPr ( cmplx_mx ) } ;
double * cmplx_imag { mxGetPi ( cmplx_mx ) } ;
# endif
double * real { mxGetPr ( real_mx ) } ;
for ( std : : conditional_t < sparse , mwSize , size_t > i { 0 } ;
i < [ & ] { if constexpr ( sparse ) return mxGetNzmax ( cmplx_mx ) ; else return mxGetNumberOfElements ( cmplx_mx ) ; } ( ) ; // Use a lambda instead of the ternary operator to have the right type (there is no constexpr ternary operator)
i + + )
# if MX_HAS_INTERLEAVED_COMPLEX
if ( cmplx [ i ] . imag = = 0.0 )
real [ i ] = cmplx [ i ] . real ;
# else
if ( cmplx_imag [ i ] = = 0.0 )
real [ i ] = cmplx_real [ i ] ;
# endif
else
real [ i ] = std : : numeric_limits < double > : : quiet_NaN ( ) ;
mxDestroyArray ( cmplx_mx ) ;
return real_mx ;
}