Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command-line control over scalar-valued set routines #662

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion examples/arkode/C_serial/ark_analytic.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ static int check_ans(N_Vector y, sunrealtype t, sunrealtype rtol,
sunrealtype atol);

/* Main Program */
int main(void)
int main(int argc, char* argv[])
{
/* general problem parameters */
sunrealtype T0 = SUN_RCONST(0.0); /* initial time */
Expand Down Expand Up @@ -130,6 +130,10 @@ int main(void)
flag = ARKodeSetLinear(arkode_mem, 0);
if (check_flag(&flag, "ARKodeSetLinear", 1)) { return 1; }

/* Override any current settings with command-line options */
flag = ARKodeSetFromCommandLine(arkode_mem, argc, argv);
if (check_flag(&flag, "ARKodeSetFromCommandLine", 1)) { return 1; }

/* Open output stream for results, output comment line */
UFID = fopen("solution.txt", "w");
fprintf(UFID, "# t u\n");
Expand Down
4 changes: 4 additions & 0 deletions include/arkode/arkode.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ typedef enum
* Shared API routines
* -------------------------- */

/* Command-line control over ARKODE options */
SUNDIALS_EXPORT int ARKodeSetFromCommandLine(void* arkode_mem, int argc,
char* argv[]);
Comment on lines +218 to +219
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SUNDIALS_EXPORT int ARKodeSetFromCommandLine(void* arkode_mem, int argc,
char* argv[]);
SUNDIALS_EXPORT int ARKodeSetFromCommandLine(void* arkode_mem, int argc,
const char* argv[]);


/* Resize and Reset functions */
SUNDIALS_EXPORT int ARKodeResize(void* arkode_mem, N_Vector ynew,
sunrealtype hscale, sunrealtype t0,
Expand Down
342 changes: 342 additions & 0 deletions src/arkode/arkode_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sunadaptcontroller/sunadaptcontroller_imexgus.h>
#include <sunadaptcontroller/sunadaptcontroller_soderlind.h>
#include <sundials/sundials_math.h>
Expand All @@ -34,6 +35,347 @@
ARKODE optional input functions
===============================================================*/

/*---------------------------------------------------------------
ARKodeSetFromCommandLine:

Parses the command line to control scalar-valued ARKODE options.

(this leverages a few typedefs and static utility routines)
---------------------------------------------------------------*/
typedef int (*arkIntSetFn)(void*,int);
typedef int (*arkLongSetFn)(void*,long int);
typedef int (*arkRealSetFn)(void*,sunrealtype);
typedef int (*arkBoolSetFn)(void*);
static int CheckAndSetIntArg(ARKodeMem ark_mem, int *i, char *argv[],
const char* argtest, arkIntSetFn fname,
sunbooleantype *arg_used)
{
*arg_used = SUNFALSE;
if (strcmp(argv[*i], argtest) == 0)
{
int iarg = atoi(argv[++(*i)]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This usage of ++ is confusing. Can it be done on a separate line?

if (fname((void*) ark_mem, iarg) != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s %s",
argv[(*i)-1], argv[*i]);
return ARK_ILL_INPUT;
}
*arg_used = SUNTRUE;
}
return ARK_SUCCESS;
}

static int CheckAndSetLongArg(ARKodeMem ark_mem, int *i, char *argv[],
const char* argtest, arkLongSetFn fname,
sunbooleantype *arg_used)
{
*arg_used = SUNFALSE;
if (strcmp(argv[*i], argtest) == 0)
{
long int iarg = atol(argv[++(*i)]);
if (fname((void*) ark_mem, iarg) != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s %s",
argv[(*i)-1], argv[*i]);
return ARK_ILL_INPUT;
}
*arg_used = SUNTRUE;
}
return ARK_SUCCESS;
}

static int CheckAndSetRealArg(ARKodeMem ark_mem, int *i, char *argv[],
const char* argtest, arkRealSetFn fname,
sunbooleantype *arg_used)
{
*arg_used = SUNFALSE;
if (strcmp(argv[*i], argtest) == 0)
{
sunrealtype rarg = atof(argv[++(*i)]);
if (fname((void*) ark_mem, rarg) != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s %s",
argv[(*i)-1], argv[*i]);
return ARK_ILL_INPUT;
}
*arg_used = SUNTRUE;
}
return ARK_SUCCESS;
}

static int CheckAndSetBoolArg(ARKodeMem ark_mem, int *i, char *argv[],
const char* argtest, arkBoolSetFn fname,
sunbooleantype *arg_used)
{
*arg_used = SUNFALSE;
if (strcmp(argv[*i], argtest) == 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So booleans are specified with just the flag? I think it would be preferable to have flag-value pair command line syntax for boolean options. Otherwise, a user will need to know the default and whether to override it.

{
if (fname((void*) ark_mem) != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s", argv[*i]);
return ARK_ILL_INPUT;
}
*arg_used = SUNTRUE;
}
return ARK_SUCCESS;
}

int ARKodeSetFromCommandLine(void* arkode_mem, int argc, char* argv[])
{
ARKodeMem ark_mem;
if (arkode_mem == NULL)
{
arkProcessError(NULL, ARK_MEM_NULL, __LINE__, __func__, __FILE__,
MSG_ARK_NO_MEM);
return (ARK_MEM_NULL);
}
ark_mem = (ARKodeMem)arkode_mem;

/* Set list of integer command-line arguments, and the corresponding set routine */
int num_int_keys = 15;
const char* int_keys[] = {"arkode.order",
"arkode.interpolant_degree",
"arkode.linear",
"arkode.autonomous",
"arkode.deduce_implicit_rhs",
"arkode.lsetup_frequency",
"arkode.predictor_method",
"arkode.max_nonlin_iters",
"arkode.max_hnil_warns",
"arkode.interpolate_stop_time",
"arkode.max_num_constr_fails",
"arkode.adaptivity_adjustment",
"arkode.small_num_efails",
"arkode.max_err_test_fails",
"arkode.max_conv_fails"};
arkIntSetFn int_set[] = {ARKodeSetOrder,
ARKodeSetInterpolantDegree,
ARKodeSetLinear,
ARKodeSetAutonomous,
ARKodeSetDeduceImplicitRhs,
ARKodeSetLSetupFrequency,
ARKodeSetPredictorMethod,
ARKodeSetMaxNonlinIters,
ARKodeSetMaxHnilWarns,
ARKodeSetInterpolateStopTime,
ARKodeSetMaxNumConstrFails,
ARKodeSetAdaptivityAdjustment,
ARKodeSetSmallNumEFails,
ARKodeSetMaxErrTestFails,
ARKodeSetMaxConvFails};
Comment on lines +139 to +169
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int num_int_keys = 15;
const char* int_keys[] = {"arkode.order",
"arkode.interpolant_degree",
"arkode.linear",
"arkode.autonomous",
"arkode.deduce_implicit_rhs",
"arkode.lsetup_frequency",
"arkode.predictor_method",
"arkode.max_nonlin_iters",
"arkode.max_hnil_warns",
"arkode.interpolate_stop_time",
"arkode.max_num_constr_fails",
"arkode.adaptivity_adjustment",
"arkode.small_num_efails",
"arkode.max_err_test_fails",
"arkode.max_conv_fails"};
arkIntSetFn int_set[] = {ARKodeSetOrder,
ARKodeSetInterpolantDegree,
ARKodeSetLinear,
ARKodeSetAutonomous,
ARKodeSetDeduceImplicitRhs,
ARKodeSetLSetupFrequency,
ARKodeSetPredictorMethod,
ARKodeSetMaxNonlinIters,
ARKodeSetMaxHnilWarns,
ARKodeSetInterpolateStopTime,
ARKodeSetMaxNumConstrFails,
ARKodeSetAdaptivityAdjustment,
ARKodeSetSmallNumEFails,
ARKodeSetMaxErrTestFails,
ARKodeSetMaxConvFails};
static const char* int_keys[] = {"arkode.order",
"arkode.interpolant_degree",
"arkode.linear",
"arkode.autonomous",
"arkode.deduce_implicit_rhs",
"arkode.lsetup_frequency",
"arkode.predictor_method",
"arkode.max_nonlin_iters",
"arkode.max_hnil_warns",
"arkode.interpolate_stop_time",
"arkode.max_num_constr_fails",
"arkode.adaptivity_adjustment",
"arkode.small_num_efails",
"arkode.max_err_test_fails",
"arkode.max_conv_fails"};
static arkIntSetFn int_set[] = {ARKodeSetOrder,
ARKodeSetInterpolantDegree,
ARKodeSetLinear,
ARKodeSetAutonomous,
ARKodeSetDeduceImplicitRhs,
ARKodeSetLSetupFrequency,
ARKodeSetPredictorMethod,
ARKodeSetMaxNonlinIters,
ARKodeSetMaxHnilWarns,
ARKodeSetInterpolateStopTime,
ARKodeSetMaxNumConstrFails,
ARKodeSetAdaptivityAdjustment,
ARKodeSetSmallNumEFails,
ARKodeSetMaxErrTestFails,
ARKodeSetMaxConvFails};
static const int num_int_keys = sizeof(int_keys) / sizeof(*int_keys);

and similar changes for code below...
To avoid issues with maintaining consistent lengths of these arrays, a cleaner solution might be to have a single array of struct { char* name; arkIntSetFn fun}.
Alternatively, this seems like an ideal place to use SUNDIALS' hashmap.


int num_long_keys = 1;
const char* long_keys[] = {"arkode.max_num_steps"};
arkLongSetFn long_set[] = {ARKodeSetMaxNumSteps};

int num_real_keys = 18;
const char* real_keys[] = {"arkode.nonlin_crdown",
"arkode.nonlin_rdiv",
"arkode.delta_gamma_max",
"arkode.nonlin_conv_coef",
"arkode.init_step",
"arkode.min_step",
"arkode.max_step",
"arkode.stop_time",
"arkode.fixed_step",
"arkode.step_direction",
"arkode.cfl_fraction",
"arkode.safety_factor",
"arkode.error_bias",
"arkode.max_growth",
"arkode.min_reduction",
"arkode.max_first_growth",
"arkode.max_efail_growth",
"arkode.max_cfail_growth"};
arkRealSetFn real_set[] = {ARKodeSetNonlinCRDown,
ARKodeSetNonlinRDiv,
ARKodeSetDeltaGammaMax,
ARKodeSetNonlinConvCoef,
ARKodeSetInitStep,
ARKodeSetMinStep,
ARKodeSetMaxStep,
ARKodeSetStopTime,
ARKodeSetFixedStep,
ARKodeSetStepDirection,
ARKodeSetCFLFraction,
ARKodeSetSafetyFactor,
ARKodeSetErrorBias,
ARKodeSetMaxGrowth,
ARKodeSetMinReduction,
ARKodeSetMaxFirstGrowth,
ARKodeSetMaxEFailGrowth,
ARKodeSetMaxCFailGrowth};

int num_bool_keys = 4;
const char* bool_keys[] = {"arkode.nonlinear",
"arkode.clear_stop_time",
"arkode.no_inactive_root_warn",
"arkode.reset_accumulated_error"};
arkBoolSetFn bool_set[] = {ARKodeSetNonlinear,
ARKodeClearStopTime,
ARKodeSetNoInactiveRootWarn,
ARKodeResetAccumulatedError};
Steven-Roberts marked this conversation as resolved.
Show resolved Hide resolved

int i, j, retval;
for (i = 1; i < argc; i++)
{
sunbooleantype arg_used = SUNFALSE;

/* skip command-line arguments that do not begin with "arkode." */
if (strncmp(argv[i], "arkode.", 7) != 0) { continue; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we filter by the arkode. prefix here, I think it's redundant to include it in all the array entries above. We would just need to shift the string pointer below.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly out of scope for this initial PR, but in order to support full configuration over an MRI or operator splitting method where they may be several arkode instances, we may need to support user-specified prefixes. For example, arkode.order 2 partition1.order 2 partition2.linear 1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removes "magic constant"

Suggested change
if (strncmp(argv[i], "arkode.", 7) != 0) { continue; }
static const char *prefix = "arkode.";
if (strncmp(argv[i], prefix, strlen(prefix)) != 0) { continue; }


/* check all "int" command-line options */
for (j = 0; j < num_int_keys; j++)
{
if (CheckAndSetIntArg(ark_mem, &i, argv, int_keys[j],
int_set[j], &arg_used) != ARK_SUCCESS)
{
return ARK_ILL_INPUT;
}
if (arg_used) break;
}
if (arg_used) continue;

/* check all long int command-line options */
for (j = 0; j < num_long_keys; j++)
{
if (CheckAndSetLongArg(ark_mem, &i, argv, long_keys[j],
long_set[j], &arg_used) != ARK_SUCCESS)
{
return ARK_ILL_INPUT;
}
if (arg_used) break;
}
if (arg_used) continue;

/* check all real command-line options */
for (j = 0; j < num_real_keys; j++)
{
if (CheckAndSetRealArg(ark_mem, &i, argv, real_keys[j],
real_set[j], &arg_used) != ARK_SUCCESS)
{
return ARK_ILL_INPUT;
}
if (arg_used) break;
}
if (arg_used) continue;

/* check all bool command-line options */
for (j = 0; j < num_bool_keys; j++)
{
if (CheckAndSetBoolArg(ark_mem, &i, argv, bool_keys[j],
bool_set[j], &arg_used) != ARK_SUCCESS)
{
return ARK_ILL_INPUT;
}
if (arg_used) break;
}
if (arg_used) continue;

/*** handle all remaining command-line options ***/

if (strcmp(argv[i], "arkode.interpolant_type") == 0)
{
i++;
retval = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
retval = 1;
retval = ARK_ILL_INPUT;

if (strcmp(argv[i], "ARK_INTERP_HERMITE") == 0)
{
retval = ARKodeSetInterpolantType(arkode_mem, ARK_INTERP_HERMITE);
}
else if (strcmp(argv[i], "ARK_INTERP_LAGRANGE") == 0)
{
retval = ARKodeSetInterpolantType(arkode_mem, ARK_INTERP_LAGRANGE);
}
else if (strcmp(argv[i], "ARK_INTERP_NONE") == 0)
{
retval = ARKodeSetInterpolantType(arkode_mem, ARK_INTERP_NONE);
}
if (retval != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s %s",
argv[i-1], argv[i]);
return ARK_ILL_INPUT;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this function always returns ARK_ILL_INPUT on a failure. If the input was ok, but a function like ARKodeSetInterpolantType failed for a different reason, e.g., some internal interpolation memory was corrupted, it would be more informative to pass along that error.

}
arg_used = SUNTRUE;
continue;
}

if (strcmp(argv[i], "arkode.tolerances") == 0)
{
sunrealtype rtol = atof(argv[++i]);
sunrealtype atol = atof(argv[++i]);
retval = ARKodeSStolerances(arkode_mem, rtol, atol);
if (retval != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s %s %s",
argv[i-2], argv[i-1], argv[i]);
return ARK_ILL_INPUT;
}
arg_used = SUNTRUE;
continue;
}

if (strcmp(argv[i], "arkode.fixed_step_bounds") == 0)
{
sunrealtype lb = atof(argv[++i]);
sunrealtype ub = atof(argv[++i]);
retval = ARKodeSetFixedStepBounds(arkode_mem, lb, ub);
if (retval != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s %s %s",
argv[i-2], argv[i-1], argv[i]);
return ARK_ILL_INPUT;
}
arg_used = SUNTRUE;
continue;
}

if (strcmp(argv[i], "arkode.accum_error_type") == 0)
{
i++;
retval = 1;
if (strcmp(argv[i], "ARK_ACCUMERROR_NONE") == 0)
{
retval = ARKodeSetAccumulatedErrorType(arkode_mem, ARK_ACCUMERROR_NONE);
}
else if (strcmp(argv[i], "ARK_ACCUMERROR_MAX") == 0)
{
retval = ARKodeSetAccumulatedErrorType(arkode_mem, ARK_ACCUMERROR_MAX);
}
else if (strcmp(argv[i], "ARK_ACCUMERROR_SUM") == 0)
{
retval = ARKodeSetAccumulatedErrorType(arkode_mem, ARK_ACCUMERROR_SUM);
}
else if (strcmp(argv[i], "ARK_ACCUMERROR_AVG") == 0)
{
retval = ARKodeSetAccumulatedErrorType(arkode_mem, ARK_ACCUMERROR_AVG);
}
if (retval != ARK_SUCCESS)
{
arkProcessError(ark_mem, ARK_ILL_INPUT, __LINE__, __func__, __FILE__,
"error setting command-line argument: %s %s",
argv[i-1], argv[i]);
return ARK_ILL_INPUT;
}
arg_used = SUNTRUE;
continue;
}

/* warn for uninterpreted arkode.X arguments */
arkProcessError(ark_mem, ARK_WARNING, __LINE__, __func__, __FILE__,
"WARNING: argument %s was not handled\n", argv[i]);
}

return (ARK_SUCCESS);
}

/*---------------------------------------------------------------
ARKodeSetDefaults:

Expand Down
Loading