Integration of Modern NJOY21 Component¶
When a new component (i.e., module) is modernized and added to NJOY21, we need to tell NJOY21 how to link to the component and what the new component needs to accept as arguments so that it can do what it needs to do.
In this document, I use the THERMR component as an example.
Summary¶
In summary here are the steps that need to be done to integrate a new component into NJOY21. Each of these steps is covered in detail in this document.
- Teach
lipservice
how to convert input arguments to JSON, e.g.,THERMR
- Remove module name (
THERMR
) fromsetupLegacyDirectory
call. - Add module name (
THERMR
) tosetupModernDirectory
call. - Define modern “sequence” routine for new component.
- Create
THERMR
class (in THERMR repository) with call operator,operator()
, taking twonlohmann::json
arguments. - Add new component (i.e., THERMR repository) as git submodule to NJOY21.
1. Passing input arguments to modern component¶
Let’s look at a typical input for THERMR:
thermr
0 -22 -24 /
0 1306 8 2 1 0 0 1 221 2 /
350.0 450.0 /
0.05 1.2 /
In NJOY21, the input deck is read by lipservice. In order to pass the arguments to the modern component we have to convert them into a JSON object. (Arguments are passed to legacy modules in some other magical fashion.) The JSON object for this input looks like:
{
"nendf": 0,
"nin": -22,
"nout": -24,
"matde": 0,
"matdp": 1306,
"nbin": 8,
"ntemp": 2,
"iin": 1,
"icoh": 0,
"iform": 0,
"natom": 1,
"mtref": 221,
"iprint": 2,
"tempr": [ 350.0, 450.0 ],
"tol": 0.05,
"emax": 1.2
}
A few things to note:
- The keys are the same argument names as in the NJOY2016 documentation (and likely the code).
- There are no “cards” in the JSON object.
- Notice that the temperatures are turned into a JSON array
[ 350.0, 450.0 ]
.
Conversion from lipservice
to JSON¶
We are using the excellent JSON library by Niels Lohmann for C++ JSON capabilities. That library provides abilities to convert custom objects into a JSON object. In the lipservice
repository is demonstration of how this is done for the THERMR module in file (shown below).
Note that there is a to_json
function for each THERMR card and lastly a to_json
function for the lipservice::THERMR
object itself.
inline void to_json( nlohmann::json& JSON, const THERMR::Card1& card1 ) {
JSON = {
{ "nendf", card1.nendf.value },
{ "nin", card1.nin.value },
{ "nout", card1.nout.value }
};
}
inline void to_json( nlohmann::json& JSON, const THERMR::Card2& card2 ) {
JSON = {
{ "matde", card2.matde.value },
{ "matdp", card2.matdp.value },
{ "nbin", card2.nbin.value },
{ "ntemp", card2.ntemp.value },
{ "iin", card2.iin.value },
{ "icoh", card2.icoh.value },
{ "iform", card2.iform.value },
{ "natom", card2.natom.value },
{ "mtref", card2.mtref.value },
{ "iprint", card2.iprint.value }
};
}
inline void to_json( nlohmann::json& JSON, const THERMR::Card3& card3 ) {
JSON = { { "tempr", card3.tempr.value } };
}
inline void to_json( nlohmann::json& JSON, const THERMR::Card4& card4 ) {
JSON = {
{ "tol", card4.tol.value },
{ "emax", card4.emax.value }
};
}
inline void to_json( nlohmann::json& JSON, const THERMR& thermr ) {
JSON = thermr.card1;
JSON.update( thermr.card2 );
JSON.update( thermr.card3 );
JSON.update( thermr.card4 );
}
This all goes in src/lipservice/src/THERMR.hpp
in lipservice
. An include statement needs to be added to src/lipservice/src/to_json.hpp
#include "lipservice/src/THERMR.hpp"
2. Modifying routines to set legacy and modern directories¶
When NJOY21 executes, it sets up some “directories” (these are not the same as folders on your computer), a directory for the legacy modules and a directory for the modern components. It does this in the setupLegacyDirectory
and setupModernDiretory
respectively.
In file src/njoy21/Driver/Factory/src/setupLegacyDirectory.hpp
:
static Directory setupLegacyDirectory( CommandLine& commandLine ){
return ( commandLine.legacySwitch ) ?
Directory( { "MODER", "RECONR", "BROADR", "PURR", "UNRESR", "ACER",
"GASPR", "HEATR", "GROUPR", "VIEWR", "MIXR", "DTFR",
"THERMR", "LEAPR", "RESXSR", "MATXSR", "GAMINR", "PLOTR",
"COVR", "WIMSR", "POWR", "CCCCR", "ERRORR" } ):
Directory( { "MODER", "RECONR", "BROADR", "PURR", "UNRESR", "ACER",
"GASPR", "HEATR", "GROUPR", "VIEWR", "MIXR", "DTFR",
"LEAPR", "RESXSR", "MATXSR", "GAMINR", "PLOTR",
"COVR", "WIMSR", "POWR", "CCCCR", "ERRORR" } );
}
Please note that "THERMR"
occurs in the first Directory
function call, but not in the second. When a modern component is added, the name of the new component needs to be removed from this second Directory
call.
In file src/njoy21/Driver/Factory/src/setupModernDirectory.hpp
:
static Directory setupModernDirectory( CommandLine& commandLine ){
return ( commandLine.legacySwitch ) ?
Directory() :
Directory( { "THERMR" } );
}
When a modern component is added, the name of the new component needs to be added to this second Directory
call.
4. Define a modern “sequence”¶
This part is really easy. In file src/njoy21/modern/Sequence/routines.hpp
just add the line DEFINE_ROUTINE( THERMR )
at the end of the file (but before the line #undef DEFINE_ROUTINE
).
#define DEFINE_ROUTINE( MODULE ) \
struct MODULE : public interface::Routine { \
nlohmann::json j##MODULE; \
\
template< typename Char > \
MODULE( lipservice::iRecordStream< Char >& stream ){ \
lipservice::MODULE command( stream ); \
this->j##MODULE = command; \
} \
void operator()( std::ostream& output, \
std::ostream& error, \
const nlohmann::json& args ){ \
njoy::MODULE::MODULE{}( std::move( this->j##MODULE ), \
output, error, \
args ); \
} \
};
DEFINE_ROUTINE( THERMR )
#undef DEFINE_ROUTINE
5. Create a functor for the component¶
In src/njoy21/modern/Sequence/routines.hpp
we can see (although it’s a bit obscure) that when NJOY21 calls the new component, it does so by calling the call operator on an object with the same name as the new component.
class THERMR {
public:
void operator()( const nlohmann::json& njoyArgs,
std::ostream& output,
std::ostream& error,
const nlohmann::json& args ){
// Do something here
}
};
This call operator does everything that needs to be done for the modern component. The njoyArgs
argument is the JSON object that was created by lipservice as described earlier. To access the different members, simply use the bracket operator:
njoyArgs[ "iprint" ];
What is done with the parameters in njoyArgs
is up to whoever wrote the component.
The output
argument is where messages about the processing should be “printed”. In Legacy NJOY, this would be the output
file. In NJOY21, the name of this file is specified using the -o
or --output
command-line argument.
The error
argument is similar to the output
argument, except that error messages are written here. Error messages are messages written before NJOY crashes (hopefully gracefully).
The args
argument is another JSON object that contains an arbitrary set of options that are passed to every modern component. Right now this is just a placeholder for unknown future needs.
When that function finishes, NJOY21 continues by running the next module given in the input deck, so everything that needs to be done with the modern component must be taken care of in this call operator. Likely, this function just calls other functions to do the real work.
6. Adding new component as submodule to NJOY21¶
Once the new component has been implemented (in a separate repository) it needs to be added to NJOY21.
Since we are currently improving our build system, the steps for this are still being developed.