diff --git a/bld/tnx1v4/patch.input.128 b/bld/tnx1v4/patch.input.128 new file mode 100644 index 00000000..94bc2ccc --- /dev/null +++ b/bld/tnx1v4/patch.input.128 @@ -0,0 +1,54 @@ + npes npe mpe idm jdm ibig jbig nreg minsea maxsea avesea + 128 6 22 360 385 60 18 2 0 1080 707 + +ispt( 1) = 57 61 0 0 273 301 +iipe( 1) = 4 23 0 0 28 22 +ispt( 2) = 1 61 178 181 273 301 +iipe( 2) = 60 51 3 3 28 60 +ispt( 3) = 1 61 121 181 241 301 +iipe( 3) = 60 60 60 60 60 60 +ispt( 4) = 1 61 121 181 241 301 +iipe( 4) = 60 60 60 60 60 60 +ispt( 5) = 1 61 121 181 241 301 +iipe( 5) = 60 60 60 60 60 60 +ispt( 6) = 1 61 121 181 241 301 +iipe( 6) = 60 60 60 60 60 60 +ispt( 7) = 1 61 121 181 241 301 +iipe( 7) = 60 60 60 60 60 60 +ispt( 8) = 1 67 121 181 241 301 +iipe( 8) = 41 54 60 60 60 60 +ispt( 9) = 1 71 121 181 241 301 +iipe( 9) = 35 50 60 60 60 60 +ispt( 10) = 1 68 121 181 241 301 +iipe( 10) = 32 53 60 60 60 60 +ispt( 11) = 1 61 121 181 241 301 +iipe( 11) = 60 60 60 60 60 60 +ispt( 12) = 1 61 121 181 241 301 +iipe( 12) = 60 60 60 60 60 60 +ispt( 13) = 1 61 154 181 241 301 +iipe( 13) = 60 56 27 60 60 60 +ispt( 14) = 1 61 149 181 241 301 +iipe( 14) = 60 37 32 60 60 60 +ispt( 15) = 1 61 127 181 241 301 +iipe( 15) = 60 41 54 60 60 60 +ispt( 16) = 1 61 121 229 241 301 +iipe( 16) = 60 60 47 12 60 60 +ispt( 17) = 28 61 121 238 241 301 +iipe( 17) = 33 60 32 3 60 47 +ispt( 18) = 16 61 121 0 247 301 +iipe( 18) = 45 60 18 0 54 43 +ispt( 19) = 16 61 121 0 249 301 +iipe( 19) = 45 60 26 0 52 31 +ispt( 20) = 20 61 121 238 241 301 +iipe( 20) = 41 60 29 3 60 24 +ispt( 21) = 24 61 121 224 241 301 +iipe( 21) = 37 60 34 17 60 32 +ispt( 22) = 1 61 121 181 241 301 +iipe( 22) = 60 60 60 60 60 60 + +jspt( 1) = 1 18 35 52 69 86 104 122 + 140 158 176 194 212 230 248 266 + 284 301 318 335 352 369 +jjpe( 1) = 17 17 17 17 17 18 18 18 + 18 18 18 18 18 18 18 18 + 17 17 17 17 17 17 diff --git a/cime_config/ParamGen/README.ipynb b/cime_config/ParamGen/README.ipynb new file mode 100644 index 00000000..874f55be --- /dev/null +++ b/cime_config/ParamGen/README.ipynb @@ -0,0 +1,1100 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ParamGen Quickstart Guide\n", + "Alper Altuntas, NCAR\\\n", + "Boulder, CO - 2021" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Introduction\n", + "\n", + "ParamGen is a lightweight, generic Python module for generating runtime parameters for earth system modeling applications. The module supports arbitrary Python expressions for the specification of parameter values. This provides a high level of flexibility and genericity.\n", + "\n", + "ParamGen infers the values of model parameters from inclusive sets of *default parameters databases* (DPD) to be put together and maintained by the model developers. These databases are typically stored in a file written in a markup language such as xml, yaml or json. ParamGen is generic, i.e., it is agnostic of any details of a particular modeling framework, model component, or input/output format. By default, the base ParamGen class supports xml, yaml and json as DPD (input) format. The only out-of-the-box output format, on the other hand, is the Fortran namelist format. New input and output formats can easily be introduced by application developers via class inheritance as will be discussed in this document. \n", + "\n", + "The primary property of a ParamGen instance is its `.data` member, which is of type Python dictionary, i.e., a collection of key-value pairs. When a ParamGen instance gets created, a dictionary must be provided to the ParamGen constructor to be accepted as its initial `.data`. This initial dictionary corresponds to the DPD, which may be read from xml, yaml, json, etc.\n", + "\n", + "In the simplest case, the keys correspond to parameter names and the values correspond to parameter values. In a more involved case, the `.data` member may be formed as a nested dictionary for grouping model parameters into seperate namelist modules. Moreover, the keys of the `.data` member may consist of logical expressions, i.e., *guards*. The notion of guards is one of the most important concepts in ParamGen. A *guard* is a proposition of a parameter value (similar to how guards are propositions of commands in Dijkstra's Guarded Command Language). Take the following data, for instance:\n", + "\n", + "```\n", + "NIGLOBAL:\n", + " $OCN_GRID == \"gx1v6\":\n", + " 320\n", + " $OCN_GRID == \"tx0.66v1\": \n", + " 540\n", + "...\n", + "```\n", + "\n", + "In the above nested dictionary, `NIGLOBAL` is interpreted as one of the parameter names. Within the inner dictionary, however, we have two keys, both of which are logical expressions. These logical expressions, or guards, are regarded as propositions of the values following them. After the instantiation, the `.reduce()` method may be called to evaluate the guards and determine the values of model parameters. In the above example, assuming the expandable variable `OCN_GRID` is `\"tx0.66v1\"`, calling the reduce method results in: \n", + "\n", + "```\n", + "NIGLOBAL:\n", + " 540\n", + "...\n", + "```\n", + "\n", + "Finally, the `.write()` method may be called to write the set of parameters in a desired format.\n", + "\n", + "*Note: not sure about the \"default parameters database\" (DPD) term. Any alternative suggestions welcome.* -aa" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. ParamGen in Action\n", + "\n", + "#### Obtaining ParamGen\n", + "\n", + "Although ParamGen is model-agnostic, it is currently distributed within an experimental CESM fork. To obtain this CESM version, run the following commands:\n", + "\n", + "```\n", + "git clone https://github.com/alperaltuntas/CESM.git -b paramGenBeta\n", + "(cd CESM; ./manage_externals/checkout_externals -o)\n", + "```\n", + "\n", + "In the above CESM sandbox, ParamGen is located in `CESM/cime/scripts/lib/CIME/ParamGen`\n", + "\n", + "#### Importing ParamGen class\n", + "\n", + "The first step of working with ParamGen is to import the module. \n", + "To import this experimental version of ParamGen module:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from paramgen import ParamGen" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: In the case of a CESM model, ParamGen would be imported from a buildnml script. To do so, one would first append the ParamGen directory to the PATH. See `CESM/components/mom/cime_config/buildnml` as an example." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Instantiating a ParamGen object:\n", + "ParamGen constructor expects a `data` argument, that is a Python dictionary which may be nested or not. This dictionary corresponds to the default parameters database (DPD) that is the collection of parameter name-value pairs for all possible configurations. Let's first define a simple Python dictionary containing three variables `X`, `Y`, and `Z`:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "DPD_dict = {\"X\" : 1.0,\n", + " \"Y\" : True,\n", + " \"Z\" : \"foo\" }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, create a ParamGen instance with this DPD dictionary:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "pg = ParamGen(DPD_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'X': 1.0, 'Y': True, 'Z': 'foo'}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now call the reduce method to generate the final version of `.data`:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "pg.reduce()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'X': 1.0, 'Y': True, 'Z': 'foo'}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As expected, the reduced data is not any different from the initial data we passed to ParamGen constructor. The `.reduce()` method makes a difference only when the initial data contains conditionals, variable expansion, or Python expressions. Before describing these mechanisms, let's generate the same ParamGen instance via yaml and json formats:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Instantiating a ParamGen object via yaml or json:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "ParamGen can be instantiated via yaml or json files using the following methods:\n", + "\n", + "- `.from_yaml()`\n", + "- `.from_json()`\n", + "\n", + "\n", + "Under the hood, these methods simply create a Python dictionary from files with these formats and then call the ParamGen constructor with the generated dictionary." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### yaml" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing DPD.yaml\n" + ] + } + ], + "source": [ + "%%writefile DPD.yaml\n", + "X: 1.0\n", + "Y: True\n", + "Z: foo" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'X': 1.0, 'Y': True, 'Z': 'foo'}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# (2) Create a ParamGen instance:\n", + "pg = ParamGen.from_yaml(\"DPD.yaml\")\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### json" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing DPD.json\n" + ] + } + ], + "source": [ + "%%writefile DPD.json\n", + "{\n", + " \"X\": 1.0,\n", + " \"Y\": true,\n", + " \"Z\": \"foo\"\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'X': 1.0, 'Y': True, 'Z': 'foo'}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# (2) Create a ParamGen instance:\n", + "pg = ParamGen.from_json(\"DPD.json\")\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Instantiating a ParamGen object via XML:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In a similar fashion, a ParamGen object may be created from an XML file. However, when working with XML, a specific schema must be satisfied. See the XML_NML.ipynb document for more information on how to work with XML within the ParamGen framework.\n", + "\n", + "Out of the three commonly used markup languages, yaml has the most readible and concise syntax, especially when working with large number of parameters and nested entries. The disadvantage of yaml is that it is not distributed with the standard Python, unlike xml and json. So a third party yaml parser, e.g., PyYAML, is required.\n", + "\n", + "Instead of using these file formats, we will continue to create ParamGen instances using Python dictionaries explicitly in the remainder of this documentation. Recall that ParamGen converts these formats to a dictionary before creating an instance so the instructions below apply to all ParamGen instances regardless of which DPD format is used." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ParamGen Mechanisms\n", + "- Variable Expansion\n", + "- Guards\n", + "- Formulas\n", + "- Appending" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Variable expansion\n", + "\n", + "Similar to shell parameter expansion mechanism in Linux, The `$` character may be used to introduce expandable variables in DPDs. These variables are expanded, i.e., replaced with their values, when the `.reduce()` method is called. Variable expansion may be employed in both keys and values of DPD dictionaries. To illustrate this mechanism, we define a new ParamGen instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "pg = ParamGen({\n", + " \"${alpha}\": 1.0,\n", + " \"Y\": \"$beta\",\n", + " \"$gamma\": \"foo\"\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'${alpha}': 1.0, 'Y': '$beta', '$gamma': 'foo'}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the above ParamGen instantiation, we specify three expandable variables in keys and values: `alpha`, `beta`, `gamma`. \n", + "When expandable variables are included in the initial data, an `expand_func` must be provided. This function is required to take a string as an argument and return a scalar, i.e., a string, integer, float, or a boolean variable. The passed string corresponds to the variable name, while the return value corresponds the value of the expandable variable. A rather simple `expand_func` is defined below for demonstration purposes." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def expand_func(varname):\n", + " if varname == \"alpha\":\n", + " return \"X\"\n", + " elif varname == \"beta\":\n", + " return True\n", + " elif varname == \"gamma\":\n", + " return \"Z\"\n", + " else:\n", + " raise RuntimeError(\"Unknown variable\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "pg.reduce(expand_func)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'X': 1.0, 'Y': 'True', '\"Z\"': 'foo'}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As seen above, all the expandable variables are expanded, i.e., replaced with their respective values. Notice that variable `beta` is converted from bool to string during variable expansion. The same behavior applies to numeric variables as well. However, this behavior is not restrictive because (1) all values are converted to strings before they are written out to text files anyways and (2) all logical expressions and formulas are to be strings to be evaluated. \n", + "\n", + "**Warning:** There is a behavioral difference between specifying string variables with curly braces vs. without curly braces. When a variable of type string gets specified ***without*** curly braces, it's value is automatically enclosed by quotes when the `reduce()` method is called. However, string variables specified ***with*** curly braces are not automatically enclosed by quotes. This behavior can be observed with the variable `gamma` which expands to `'\"Z\"'`. Had we specified gamma with curly braces, i.e., `${gamma}`, the value would rather be `'Z'`, and not `'\"Z\"'`. This can be confirmed with the variable `alpha` above, which expands to `'X'`.\n", + "\n", + "This behavior is introduced in ParamGen as a means of keeping conditional expressions more concise. Compare the following two logical ParamGen expressions, which are equivalent, but the first one has expandable variables defined with curly braces. In the first version, not only do we have to explicitly enclose expandable variables with quotes (`\"${...}\"`), but also the entire expression (`'...'`) so as to make sure that YAML parser treats the entire logical formula as a single expression. In the second version, neither of the quotes is necessary, except, of course, for the literal strings `\"gx1v7\"` and `\"datm\"`.\n", + "\n", + "`' \"${OCN_GRID}\" == \"gx1v7\" and \"${COMP_ATM}\" == \"datm\" ':`\n", + "\n", + "`$OCN_GRID == \"gx1v7\" and $COMP_ATM == \"datm\":`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### CESM/CIME XML variables as expandable variables" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Within the CESM framework, CIME XML variables may easily be specified in DBDs as expandable variables. Typically, `ParamGen` is utilized in `buildnml` scripts of components. The first argument of all `buildnml` methods is the `case` variable which is an instance of `CIME.case.Case`. This CIME case object has a `.get_value()` method that returns the value of a given XML variable. This method may simply be passed to the `reduce()` method of ParamGen as an expand function:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + "def expand_func(varname):\n", + " case.get_value(varname)\n", + " \n", + "pg.reduce(expand_func)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or, more concisely:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + "pg.reduce(lambda varname: case.get_value(varname))\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Examples of this usage can be found in MOM6 implementation of ParamGen. Check out the following derived ParamGen classes of MOM6:\n", + "\n", + " - CESM/components/mom/cime_config/MOM_RPS/FType_input_nml.py\n", + " - CESM/components/mom/cime_config/MOM_RPS/FType_MOM_params.py\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Guards" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Recall that the keys of the `.data` dictionary specify the parameter names while the values correspond to the respective parameter values. Depending on the context, the keys may also be interpreted as guards, i.e., propositions of parameter values. The keys are interpreted as guards if all the keys at a certain level are logical expressions that evaluate to True or False. A data dictionary *without* any guards:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "dict1 = {\n", + " \"var1\": 1,\n", + " \"var2\": 2\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A data dictionary with some guards:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "dict2 = {\n", + " \"var1\": {\n", + " True: 1,\n", + " False: 0\n", + " },\n", + " \"var2\": 2\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the above dictionary `dict2`, the variable `var1` has two options, `1` and `0`. Which value gets picked for \"var1\" depends on the guards, i.e., the propositions `True` and `False`. Now let's create a ParamGen instance with the dictionary `dict2`:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'var1': {True: 1, False: 0}, 'var2': 2}" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg = ParamGen(dict2)\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Observe the effect of calling the reduce method below:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'var1': 1, 'var2': 2}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg.reduce()\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Logical Python expressions as guards\n", + "\n", + "The guards above are trivially specified to be `True` and `False`. In practice, however, guards are arbitrary Python expressions that evaluate to `True` or `False`. These expressions may have expandable variables, standard Python operators, method calls, etc. For an expression to be regarded as a guard, then, the expression must evaluate to `True` or `False`.\n", + "\n", + "Note: In YAML, the quotes enclosing the expressions are not necessary, since the YAML parser automatically interprets those logical expressions as strings.\n", + " \n", + "The following is an example with arbitrary Python expressions as guards:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'var1': 1, 'var2': 2}" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def expand_func(varname):\n", + " if varname == \"one\":\n", + " return 1.0\n", + " elif varname == \"two\":\n", + " return 2.0\n", + " else:\n", + " raise RuntimeError(\"Unknown variable\")\n", + " \n", + " \n", + "dict3 = {\n", + " \"var1\": {\n", + " '$one < $two' : 1,\n", + " '$one > $two' : 0\n", + " },\n", + " \"var2\": 2\n", + "}\n", + "\n", + "pg = ParamGen(dict3)\n", + "pg.reduce(expand_func)\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Guard behavior\n", + "\n", + "- If multiple guards evaluate to True, the last option gets picked. If it is desired to pick the first valid option, however, the default behavior may be changed by setting the optional `match` argument of ParamGen to `first`. For example: `pg = ParamGen(dict2, match='first')`\n", + "- If no guards evaluate to True, the parameter value gets set to `None`. In a model-specific write method, the parameters with the value `None` may, for example, be chosen to be omitted by the application developer. \n", + "- the `else` keyword evaluates to True only if all other guards evaluate to False.\n", + "- When an expandable variable is attempted to be expanded, and if the value is undefined, ParamGen throws an error. In some cases, certain expandable variables may be defined only for certain configurations. \n", + "For instance, in the below example, the variable `INIT_LAYERS_FROM_Z_FILE` is defined only if the `OCN_GRID` is one of `[\"gx1v6\", \"tx0.66v1\", \"tx0.25v1\"]`. Therefore, to avoid undefined expandable variable error, we place the `INIT_LAYERS_FROM_Z_FILE` check below the `OCN_GRID` check, as follows:\n", + "\n", + "```\n", + " tempsalt:\n", + " $OCN_GRID in [\"gx1v6\", \"tx0.66v1\", \"tx0.25v1\"]:\n", + " $INIT_LAYERS_FROM_Z_FILE == \"True\":\n", + " \"${INPUTDIR}/${TEMP_SALT_Z_INIT_FILE}\"\n", + "```\n", + "\n", + " This ensures that ParamGen attempts to expand `INIT_LAYERS_FROM_Z_FILE` variable only when `$OCN_GRID in [\"gx1v6\", \"tx0.66v1\", \"tx0.25v1\"]` evaluates to True." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Formulas" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In ParamGen, a variable value may be specified as a formula to be evaluated. This is done by setting the first character of a value to a space delimited `=` character. See the below example:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'var1': 5}" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg = ParamGen({\n", + " 'var1' : '= 2+3'\n", + "})\n", + "pg.reduce()\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that formulas may also include expandable variables:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'var1': 2.5}" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg = ParamGen({\n", + " 'var1' : '= (2+3) / $two'\n", + "})\n", + "pg.reduce(expand_func)\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Appending" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `append()` method of ParamGen adds the data of a given ParamGen instance to the existing data. If a data entry with the same name already exists, it's value gets overriden with the new value. Otherwise, the new data entry is simply appended to the existing data." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 3, 'c': 4}" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg1 = ParamGen({'a':1, 'b':2})\n", + "pg2 = ParamGen({'b':3, 'c':4})\n", + "pg1.append(pg2)\n", + "pg1.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Special Use Cases\n", + "\n", + "### Referencing across multiple ParamGen instances\n", + "\n", + "The genericity that comes with the custom expand functions allows us to reference the data of a ParamGen instance in another ParamGen instance. To illustrate this use case, we define two ParamGen instances:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "pg1 = ParamGen({\n", + " 'var1' : 'foo',\n", + " 'var2' : 'bar'\n", + "})\n", + "\n", + "pg2 = ParamGen({\n", + " 'var3': '${var1}${var2}' \n", + "})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice above that the second ParamGen instance, pg2, data includes references to variables defined in pg1 data. Now let's reduce the data of pg2 and pass a lambda function that returns the values of pg1 variables:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "pg2.reduce(lambda varname: pg1.data[varname])" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'var3': 'foobar'}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg2.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note:** Cross-referencing, i.e., references to variables within the same instance, is not supported. (May be added later on if need be. -aa)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Custom value inference\n", + "\n", + "More involved expand functions may allow higher customizations. In the below example, for instance, we read in an xarray dataset `ds` and set the value of an expandable variable `my_fields_list` to the list of all variables in `ds`, that is `\"lat air lon time\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "pg1 = ParamGen({\n", + " 'var1' : 'foo',\n", + " 'var2' : 'bar'\n", + "})\n", + "\n", + "pg2 = ParamGen({\n", + " 'param1': '${var1}${var2}',\n", + " 'param2': '$my_fields_list'\n", + "})\n", + "\n", + "def expand_function(varname):\n", + " if varname in pg1.data:\n", + " return pg1.data[varname]\n", + " elif varname == \"my_fields_list\":\n", + " try:\n", + " import xarray as xr\n", + " ds = xr.tutorial.load_dataset(\"air_temperature\")\n", + " return ' '.join([var for var in ds.variables])\n", + " except:\n", + " print(\"Cannot load xarray module. Skipping...\")\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "pg2.reduce(expand_function)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'param1': 'foobar', 'param2': '\"lat air lon time\"'}" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg2.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Regular expression searches as guards\n", + "\n", + "In some cases, it may be desirable to do regex searches as opposed to simpler string comparisons. One such example use case is provided below:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "pg = ParamGen({\n", + " 'USE_MARBL_TRACERS': {\n", + " 'bool(re.search(\"MOM6%[^_]*MARBL\", $COMPSET ))': True,\n", + " 'else': False\n", + " }\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'USE_MARBL_TRACERS': True}" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pg.reduce(lambda varname:'1850_DATM%NYF_SLND_DICE%SSMI_MOM6%MARBL_DROF%NYF_SGLC_SWAV')\n", + "pg.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice how the `re.search()` method is used above in the first value guard. The guard evaluates to true since the `re.search()` method is able to find `\"MOM6%[^_]*MARBL\"` regex pattern in the specified COMPSET: \n", + "`1850_DATM%NYF_SLND_DICE%SSMI_MOM6%MARBL_DROF%NYF_SGLC_SWAV`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Notes on ParamGen for MOM6 in CESM\n", + "\n", + "Here, we briefly describe how ParamGen is used within CESM to generate MOM6 runtime parameters. Called from the `buildnml` script of MOM6+CESM, the ParamGen module is used to generate the four main MOM6 runtime input files:\n", + "\n", + " 1. **MOM_input:** Default MOM6 runtime parameters. The file syntax is based on the simple `key = value` pair. Example parameter entries from a typical MOM6 experiment:\n", + " \n", + " ```\n", + " DIABATIC_FIRST = True ! If true, apply diabatic and thermodynamic processes...\n", + " DT_THERM = 3600.0 ! The thermodynamic and tracer advection time step.\n", + " MIN_SALINITY = 0.0. ! The minimum value of salinity when BOUND_SALINITY=True.\n", + " ```\n", + " \n", + " 2. **MOM_override:** An auxiliary file to override parameter values set in MOM_input\n", + " 3. **input.nml:** A file to set some general MOM6 and FMS variables. The file is in classical Fortran namelist format.\n", + " 4. **diag_table:** An input file to configure the model diagnostics. It has a relatively complex syntax. See https://mom6.readthedocs.io/en/latest/api/generated/pages/Diagnostics.html\n", + " \n", + " In addition to these files, ParamGen is used to generate the MOM6 version of the `input_data_list` file needed by CIME.\n", + " \n", + " ### Default Parameters Databases \n", + " \n", + " For each of the input files mentioned above, we have a DBD that includes all of the default parameter values for any possible model configuration. In the case of `MOM_input` for instance, we have a DBD called `MOM_input.yaml` located in `components/mom/param_templates`. An example entry from this file:\n", + " \n", + " ```\n", + " DT_THERM:\n", + " description: |\n", + " \"[s] default = 3600.0\n", + " The thermodynamic and tracer advection time step.\n", + " value:\n", + " $OCN_GRID == \"MISOMIP\": 1800.0\n", + " else: >\n", + " = ( ( $NCPL_BASE_PERIOD ==\"decade\") * 86400.0 * 3650 +\n", + " ( $NCPL_BASE_PERIOD ==\"year\") * 86400.0 * 365 +\n", + " ( $NCPL_BASE_PERIOD ==\"day\") * 86400.0 +\n", + " ( $NCPL_BASE_PERIOD ==\"hour\") * 3600.0 ) / $OCN_NCPL\n", + "```\n", + "\n", + "In the above entry, the default value of the runtime parameter `DT_THERM` is specified, which depends on a few CIME variables such as `OCN_GRID`, and `OCN_NCPL`. Notice the usage of expandable variables, guards, and a formula.\n", + "\n", + "Assuming `$OCN_GRID != \"MISOMIP\"`, `$NCPL_BASE_PERIOD == \"day\"`, and `$OCN_NCPL==24`, the runtime parameter `DT_THERM` gets reduced to 3600.0 when `.reduce()` method is called. \n", + "\n", + "### Utilizing ParamGen class as a base\n", + "\n", + "For each of the file category, we have developed individual classes derived from the `ParamGen` class. These classes are located in `CESM/components/mom/cime_config/MOM_RPS/` and are utilized in the `buildnml` file to generate the corresponding input files. \n", + "\n", + " - FType_MOM_params.py\n", + " - FType_diag_table.py\n", + " - FType_input_data_list.py\n", + " - FType_input_nml.py\n", + "\n", + "Since the Fortran namelist syntax is already available as an out-of the box format, the most straightforward one is `FType_input_nml.py` which produces the `input.nml` file. The whole module consists of 15 lines of code:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "\n", + "CIMEROOT = os.environ.get(\"CIMEROOT\")\n", + "if CIMEROOT is None:\n", + " raise SystemExit(\"ERROR: must set CIMEROOT environment variable\")\n", + "sys.path.append(os.path.join(CIMEROOT, \"scripts\", \"lib\", \"CIME\", \"ParamGen\"))\n", + "from paramgen import ParamGen\n", + "\n", + "class FType_input_nml(ParamGen):\n", + " \"\"\"Encapsulates data and read/write methods for MOM6 (FMS) input.nml file\"\"\"\n", + "\n", + " def write(self, output_path, case):\n", + " self.reduce(lambda varname: case.get_value(varname))\n", + " self.write_nml(output_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Within the `buildnml` script, the above class is instantiated and utilized as follows:\n", + "\n", + "```\n", + "...\n", + "input_nml = FType_input_nml.from_json(input_nml_src)\n", + "input_nml.write(input_nml_dest, case)\n", + "```\n", + "\n", + "#### (todo) more descriptions and notes to come." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "base" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cime_config/ParamGen/XML_NML.ipynb b/cime_config/ParamGen/XML_NML.ipynb new file mode 100644 index 00000000..bf87f0d7 --- /dev/null +++ b/cime_config/ParamGen/XML_NML.ipynb @@ -0,0 +1,338 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# XML Namelist Format in ParamGen\n", + "Alper Altuntas, NCAR\\\n", + "Boulder, CO - 2021" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Introduction\n", + "\n", + "Here, we briefly describe a special use case of ParamGen: XML-based namelist format. This document is complementary to the README.ipynb file, which describes ParamGen in a broader context and with more detail. It is advised to read the README.ipynb file first." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. XML Namelist Template" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The YAML and JSON frontends of ParamGen are quite flexible in terms of the data layout, or schema. In the case of XML, however, we work with a predefined schema that resembles CESM's `entry_id_namelist.xsd`. The new ParamGen schema, called `entry_id_pg.xsd`, is located in `cime/scripts/lib/CIME/ParamGen/xml_schema/`\n", + "\n", + "We first write an example xml file, named `my_tamplate.xml` below that conforms to this new schema." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing my_template.xml\n" + ] + } + ], + "source": [ + "%%writefile my_template.xml\n", + "\n", + "\n", + "\n", + "\n", + " \n", + " real\n", + " setup_nml\n", + " Days per year\n", + " \n", + " 365\n", + " \n", + " \n", + "\n", + "\n", + " \n", + " logical\n", + " icefields_nml\n", + " f_anglet\n", + " \n", + " .true.\n", + " .false.\n", + " \n", + " \n", + "\n", + " \n", + " char\n", + " setup_nml\n", + " Method of ice cover initialization.\n", + " \n", + " UNSET\n", + " ${DIN_LOC_ROOT}/ice/cice/b40.t31x3.20th.cice.r.2006-01-01-00000.nc\n", + " ${DIN_LOC_ROOT}/ice/cice/b.e15.B1850G.f09_g16.pi_control.25.cice.r.0041-01-01-00000.nc\n", + " ${DIN_LOC_ROOT}/ice/cice/g.e11.G.T62_t12.002.cice.r.0016-01-01-00000.nc\n", + " ${DIN_LOC_ROOT}/ice/cice/cice5ic/r26RBRCICE5g0.cice.r.1990-09-01-00000.nc\n", + " \n", + " \n", + "\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above xml file includes three namelist variable definitions taken from CICE `namelist_definition_cice.xml`. Notice how the format is very similar to the original namelist definition format. Some of the differences between the original `entry_id_namelist.xsd` vs the new `entry_id_pg.xsd:`\n", + "\n", + "- To easily distinguish these schemas, the root element in the new schema is called `entry_id_pg`, and not `entry_id`.\n", + "- Currently, only a subset of descriptive child elements are supported for `entry_id_pg` entries. These are `type`, `group`, and `desc`. More elements may be added as needed.\n", + "- In the traditional format, value propositions would be specified with arbitrary ``, attributes, e.g., `hgrid=\"gx3v7`\". The new format also supports this specification type. And within a value entry, multiple key=value attributes may be specified, in which case they are joined with logical AND. In ParamGen implementation of XML specification, however, there is an alternative method of specifying guards, which brings about greater flexibility. \n", + "- The more flexible method is based on specifying guards via the `guard=` attribute. A `guard=` attribute can be any arbitrary Python expression that evalutes to True or False. These expressions can involve any variables (see `expand_func` description in README.ipynb) and any standard Python operators, methods, list comprehensions, etc. Notice how the `.startswith()` method is used to abbreviate the `ice_ic` value list compared to the the traditional proposition specification which would require multiple value entries for each grid starting with \"gx1v\", \"tx0.1v\", and \"ar9v3\"." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before showcasing the ParamGen module, we define an `expand_func`. Recall that ParamGen makes use of custom `expand_func` to infer the values of expandable variables. In the above xml file, we have three such variables: `cice_mode`, `ICE_GRID`, and `DIN_LOC_ROOT`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def expand_func_demo(varname):\n", + " return {\n", + " 'ICE_GRID': 'gx1v6',\n", + " 'DIN_LOC_ROOT': '/glade/p/cesmdata/cseg/inputdata',\n", + " 'cice_mode': 'thermo_only',\n", + " }[varname]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While the above `expand_func_demo` is a trivial one for demonstration purposes, the below `expand_func` is a real-world example taken from MOM6 within CESM:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def expand_func(varname):\n", + " return case.get_value(varname)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "where `case` is a CIME case object whose `get_value()` method returns the values of XML variables like `ICE_GRID`, `DIN_LOC_ROOT`, etc." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. ParamGen XML namelist format in action\n", + "\n", + "We first import the ParamGen class as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from paramgen import ParamGen" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now instantiate a ParamGen object by passing the `my_template.xml` file path to the `from_xml_nml()` method of ParamGen" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "pg = ParamGen.from_xml_nml(\"./my_template.xml\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After having instantiated the ParamGen object, we can call its `reduce` method to evaluate guards and infer final namelist variable values. Notice that we pass in the `expand_func_demo` method so ParamGen can infer the values of expandable variables such as `ICE_GRID`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "pg.reduce(expand_func_demo)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we can write out a Fortran namelist file as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "pg.write_nml(\"./my_nml_file.nml\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The resulting namelist file is as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "&setup_nml\n", + " days_per_year = 365\n", + " ice_ic = /glade/p/cesmdata/cseg/inputdata/ice/cice/b.e15.B1850G.f09_g16.pi_control.25.cice.r.0041-01-01-00000.nc\n", + "/\n", + "\n", + "&icefields_nml\n", + " f_anglet = .true.\n", + "/\n", + "\n" + ] + } + ], + "source": [ + "!cat ./my_nml_file.nml" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition to writing out the namelist file, we can access the data directly, both before and after the `reduce()` method is called. Some examples:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ".true.\n" + ] + } + ], + "source": [ + "# print out the final value of `f_anglet`:\n", + "print(pg.data['icefields_nml']['f_anglet']['values'])" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Days per year\n" + ] + } + ], + "source": [ + "# print out the description of `days_per_year`:\n", + "print(pg.data['setup_nml']['days_per_year']['desc'])" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "char\n" + ] + } + ], + "source": [ + "# print out the type of `ice_ic`:\n", + "print(pg.data['setup_nml']['ice_ic']['type'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "CMIP6 2019.10", + "language": "python", + "name": "cmip6-201910" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cime_config/ParamGen/paramgen.py b/cime_config/ParamGen/paramgen.py new file mode 100644 index 00000000..f80c5139 --- /dev/null +++ b/cime_config/ParamGen/paramgen.py @@ -0,0 +1,558 @@ +import os +import sys +import re +from copy import deepcopy +import logging +import subprocess + +try: + from paramgen_utils import is_logical_expr, is_formula, has_unexpanded_var + from paramgen_utils import eval_formula +except ModuleNotFoundError: + from CIME.ParamGen.paramgen_utils import ( + is_logical_expr, + is_formula, + has_unexpanded_var, + ) + from CIME.ParamGen.paramgen_utils import eval_formula + +#print (f"sys.version_info.major is {sys.version_info.major}") +#print (f"sys.version_info.minor is {sys.version_info.minor}") + +assert ( + sys.version_info.major == 3 and sys.version_info.minor >= 6 +), "ParamGen requires Python 3.6 or later." + +logger = logging.getLogger(__name__) + + +class ParamGen: + """ + ParamGen is a versatile, generic, lightweight base class to be used when developing namelist + and parameter generator tools for scientific modeling applications. + + Attributes + ---------- + data : dict + The data attribute to operate over. See Methods for the list of operations. + match: str + "first" or "last". + + Methods + ------- + from_json(input_path) + Reads in a given json input file and initializes a ParamGen object. + from_yaml(input_path) + Reads in a given yaml input file and initializes a ParamGen object. + """ + + def __init__(self, data_dict, match="last"): + assert isinstance( + data_dict, dict + ), "ParamGen class requires a dict as the initial data." + # self._validate_schema(data_dict) + self._original_data = deepcopy(data_dict) + self._data = deepcopy(data_dict) + self._reduced = False + self._match = match + + @property + def data(self): + """Returns the data attribute of the ParamGen instance.""" + return self._data + + @property + def reduced(self): + """Returns True if reduce() method is called for this instance of ParamGen.""" + return self._reduced + + @property + def is_empty(self): + """Returns True if the data property is empty.""" + return len(self._data) == 0 + + @classmethod + def from_json(cls, input_path, match="last"): + """ + Reads in a given json input file and initializes a ParamGen object. + + Parameters + ---------- + input_path: str + Path to json input file containing the defaults. + match: str + "first" or "last" + Returns + ------- + ParamGen + A ParamGen object with the data read from input_path. + """ + + import json + + with open(input_path) as json_file: + _data = json.load(json_file) + return cls(_data, match) + + @classmethod + def from_yaml(cls, input_path, match="last"): + """ + Reads in a given yaml input file and initializes a ParamGen object. + + Parameters + ---------- + input_path: str + Path to yaml input file containing the defaults. + match: str + "first" or "last" + Returns + ------- + ParamGen + A ParamGen object with the data read from input_path. + """ + + import yaml + + with open(input_path) as yaml_file: + _data = yaml.safe_load(yaml_file) + return cls(_data, match) + + @classmethod + def from_xml_nml(cls, input_path, match="last", no_duplicates=False): + """ + Reads in a given xml input file and initializes a ParamGen object. The XML file must conform to the + entry_id_pg.xsd schema that's defined within ParamGen. + + Parameters + ---------- + input_path: str + Path to xml input file containing the defaults. + match: str + "first" or "last" + no_duplicates: bool + If True, then any duplicate entry ids in the XML file will result in an error. + + Returns + ------- + ParamGen + A ParamGen object with the data read from input_path. + """ + + # First check whether the given xml file conforms to the entry_id_pg.xsd schema + from distutils.spawn import find_executable + + xmllint = find_executable("xmllint") + if xmllint is None: + logger.warning("Couldn't find xmllint. Skipping schema check") + else: + schema_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "xml_schema", + "entry_id_pg.xsd", + ) + xmllint_cmd = "{} --xinclude --noout --schema {} {}".format( + xmllint, schema_path, input_path + ) + + stat = subprocess.run( + xmllint_cmd, shell=True, check=True + ) + assert ( + stat.returncode == 0 + ), f"While checking file {input_path} against nml schema, run xmllint by hand using command\n{xmllint_cmd}" + + import xml.etree.ElementTree as ET + + xml_tree = ET.parse(input_path) + root = xml_tree.getroot() + data = {} + + # Loop over all entries (namelist variables) + for entry in list(root): + # where each entry corresponds to a namelist variable, i.e., parameter + param_name = entry.attrib["id"] + if no_duplicates and param_name in data: + # No duplicate namelist entries allowed, so raise error: + emsg = "Entry id '{}' listed twice in file:\n'{}'" + raise ValueError(emsg.format(param_name, input_path)) + else: + data[param_name] = {} + + if "is_inputdata" in entry.attrib: + data[param_name]["is_inputdata"] = entry.attrib["is_inputdata"] + else: + data[param_name]["is_inputdata"] = "no" + + if "modify_via_xml" in entry.attrib: + data[param_name]["modify_via_xml"] = entry.attrib["modify_via_xml"] + else: + data[param_name]["modify_via_xml"] = 'unset' + + # loop over child entries and attributes of the parameter + for child in list(entry): + if child.tag == "values": + data[param_name]["values"] = {} + values = list(child) + + # check if the values have logical guards as propositions + guards = {} + for value in values: + if "guard" in value.attrib: + assert len(value.attrib) == 1, ( + "If an explicit guard attribute is provided for a value," + + "no other attribute may be provided. Check parameter {}".format( + param_name + ) + ) + guards[value] = value.attrib["guard"] + elif len(value.attrib) > 0: + guards[value] = " and ".join( + [ + '"${{{}}}" == "{}"'.format( + str(guard_var), str(guard_val) + ) + for guard_var, guard_val in value.attrib.items() + ] + ) + else: + assert "else" not in guards.values(), ( + "Multiple values with no guards (proposition)" + + "detected in variable {}".format(param_name) + ) + guards[value] = "else" + + if len(values) == 1 and guards[values[0]] == "else": + data[param_name]["values"] = list(values)[0].text.strip() + else: + for value, guard in guards.items(): + data[param_name]["values"][guard] = value.text.strip() + + else: + # a child element other than the element (.e.g, type, desc, group, etc.) + data[param_name][child.tag] = child.text.strip() + + # now group the parameters according to their group_id's: + _data = {} # grouped data + for param_name in data: + param_group = data[param_name]["group"] + if param_group not in _data: + _data[param_group] = {} + _data[param_group][param_name] = data[param_name] + + return cls(_data, match) + + @staticmethod + def _expand_vars(expr, expand_func): + """Replaces the expandable variables with their values in a given expression (expr) of type str. + + Parameters + ---------- + expr: str + expression with expandable variables, e.g., "$OCN_GRID == "tx0.66v1" + expand_func: + a callable objects that can return the value of any expandable variable specified in expr" + Returns + ------- + an expresion of type string where the expandable variables are replaced with their values. + + Example + ------- + >>> ParamGen._expand_vars("$x + 2 == $z", (lambda var: 1 if var=='x' else 3) ) + '1 + 2 == 3' + """ + + if expand_func is None: + return expr # No expansion function is provided, so return. + + assert isinstance( + expr, str + ), "Expression passed to _expand_vars must be string." + expandable_vars = re.findall(r"(\$\w+|\${\w+\})", expr) + for word in expandable_vars: + word_stripped = ( + word.strip().replace("$", "").replace("{", "").replace("}", "") + ) + word_expanded = expand_func(word_stripped) + assert ( + word_expanded is not None + ), "Cannot determine the value of the variable: {}.".format(word) + + # enclose with quotes if expanded var is a string and is expression sans curly braces + if isinstance(word_expanded, str) and word[1] != "{": + word_expanded = '"' + word_expanded + '"' + else: + word_expanded = str(word_expanded) + + expr = re.sub( + r"(\$\b" + word_stripped + r"\b|\$\{" + word_stripped + r"\})", + word_expanded, + expr, + ) + + return expr + + @staticmethod + def _is_guarded_dict(data_dict): + """Returns true if all the keys of a dictionary are logical expressions, i.e., guards. + + Parameters + ---------- + data_dict: dict + A dictionary where the keys may be logical expressions (of type string) + + Example + ------- + >>> ParamGen._is_guarded_dict({True: 'x', 'False': 'y'}) + True + >>> ParamGen._is_guarded_dict({ "'tx0.66v1' == 'tx0.66v1'": 'x', False: 'y'}) + True + >>> ParamGen._is_guarded_dict({'i':'x', 'j':'y'}) + False + """ + if not isinstance(data_dict, dict): + return False + + keys_logical = [is_logical_expr(str(key)) for key in data_dict] + if all(keys_logical): + return True + if any(keys_logical): + raise RuntimeError( + "Only subset of the following are guarded entries, i.e., logical " + + "expressions as keys:\n\t" + + str(data_dict) + ) + return False + + def _impose_guards(self, data_dict): + + """Given a data_dict with guarded entries, evaluates the guards and returns the entry whose guard (key) + evaluates to True. If multiple guards evaluate to true, the first or the last entry with the True guard is + returned, depending on the "match" arg passed to ParamGen initializer. This method is intended to be called + from _reduce_recursive only. + + Parameters + ---------- + data_dict: dict + A dictionary whose keys are all logical expressions, i.e., guards. + + Example + ------- + >>> obj = ParamGen({1>2: 'a', 3>2: 'b'}) + >>> new_data = obj._impose_guards(obj.data) + >>> new_data + 'b' + """ + + def _eval_guard(guard): + """returns true if a guard evaluates to true.""" + assert isinstance( + guard, str + ), "Expression passed to _eval_guard must be string." + + if has_unexpanded_var(guard): + raise RuntimeError( + "The guard " + + guard + + " has an expandable variable ($var) " + + "that's not expanded yet. All variables must already be expanded before " + + "guards can be evaluated." + ) + + guard_evaluated = eval_formula(guard) + assert isinstance(guard_evaluated, bool), "Guard is not boolean: {}".format( + guard + ) + return guard_evaluated + + if not ParamGen._is_guarded_dict(data_dict): + return data_dict + + guards_eval_true = [] # list of guards that evaluate to true. + for guard in data_dict: + if guard == "else" or _eval_guard(str(guard)) is True: + guards_eval_true.append(guard) + + if len(guards_eval_true) > 1 and "else" in guards_eval_true: + guards_eval_true.remove("else") + elif len(guards_eval_true) == 0: + return None + + if self._match == "first": + return data_dict[guards_eval_true[0]] + if self._match == "last": + return data_dict[guards_eval_true[-1]] + raise RuntimeError("Unknown match option.") + + def _reduce_recursive(self, data_dict, expand_func=None): + + """A recursive method to reduce a given data_dict. This method is intended to be called by the reduce method + only. Check the docstring of the reduce method for more information.""" + + # (1) Expand variables in keys, .e.g, "$OCN_GRID" to "gx1v7": + def _expand_vars_in_keys(data_dict): + if expand_func is not None: + new_data_dict = {} + for key in data_dict: + new_key = key + if has_unexpanded_var(key): + new_key = ParamGen._expand_vars(key, expand_func) + new_data_dict[new_key] = data_dict[key] + return new_data_dict + return data_dict + + data_dict = _expand_vars_in_keys(data_dict) + + # (2) Evaluate the keys if they are all logical expressions, i.e., guards. + # Pick the value of the first or last key evaluating to True and drop everything else. + while ParamGen._is_guarded_dict(data_dict): + data_dict = self._impose_guards(data_dict) + if isinstance(data_dict, dict): + data_dict = _expand_vars_in_keys(data_dict) + + # If the data_dict is reduced to a string, expand vars and eval formulas as a last step. + if isinstance(data_dict, str): + + # (3) Expand variables in values, .e.g, "$OCN_GRID" to "gx1v7": + data_dict = ParamGen._expand_vars(data_dict, expand_func) + + # (4) Evaluate the formulas as values, e.g., "= 3+4", "= [i for i in range(5)]", etc. + if is_formula(data_dict): + data_dict = eval_formula(data_dict.strip()[1:]) + + # Continue reducing the data if it is still a dictionary (nested or not). + elif isinstance(data_dict, dict): + + # (3) Expand variables in values, .e.g, "$OCN_GRID" to "gx1v7": + for key, val in data_dict.copy().items(): + if isinstance(val, str): + data_dict[key] = ParamGen._expand_vars(val, expand_func) + + # (4) Evaluate the formulas as values, e.g., "= 3+4", "= [i for i in range(5)]", etc. + for key, val in data_dict.copy().items(): + if is_formula(val): + data_dict[key] = eval_formula(val.strip()[1:]) + + # (5) Recursively call _reduce_recursive for the remaining nested dicts before returning + keys_of_nested_dicts = [] # i.e., keys of values that are of type dict + for key, val in data_dict.items(): + if isinstance(val, dict): + keys_of_nested_dicts.append(key) + for key in keys_of_nested_dicts: + data_dict[key] = self._reduce_recursive(data_dict[key], expand_func) + + return data_dict + + def reduce(self, expand_func=None): + """ + Reduces the data of a ParamGen instances by recursively expanding its variables, + imposing conditional guards, and evaluating the formulas in values to determine + the final values of parameters. + + Parameters + ---------- + expand_func: (optional) + a callable objects that can return the value of any expandable variable specified." + + Example + ------- + >>> obj = ParamGen({1>2: 'a', 3>2: 'b'}) + >>> obj.reduce() + >>> obj.data + 'b' + """ + + assert ( + callable(expand_func) or expand_func is None + ), "expand_func argument must be a function" + assert not self.reduced, "ParamGen data already reduced." + assert not self.is_empty, "Empty ParamGen data." + + self._data = self._reduce_recursive(self._data, expand_func) + self._reduced = True + + def append(self, pg_obj): + """Adds the data of a given ParamGen instance to the self data. If a data entry already exists in self, + the value is overriden. Otherwise, the new data entry is simply added to self. + + Parameters + ---------- + pg_obj: ParamGen object + A ParamGen instance whose data is to be appended to the self data + + Example + ------- + >>> obj1 = ParamGen({'a':1, 'b':2}) + >>> obj2 = ParamGen({'b':3, 'c':4}) + >>> obj1.append(obj2) + >>> obj1.data + {'a': 1, 'b': 3, 'c': 4} + """ + + assert isinstance(pg_obj, ParamGen), "can only append ParamGen to Paramgen" + assert ( + self.reduced == pg_obj.reduced + ), "Cannot append reduced ParamGen instance to unreduced, and vice versa." + + def _append_recursive(old_dict, new_dict): + for key, val in new_dict.items(): + if key in old_dict: + old_val = old_dict[key] + if isinstance(val, dict) and isinstance(old_val, dict): + _append_recursive(old_dict[key], new_dict[key]) + else: + old_dict[key] = new_dict[key] + else: + old_dict[key] = new_dict[key] + + _append_recursive(self._data, pg_obj._data) + + def reset(self): + """Resets the ParamGen object to its initial state, i.e., undoes the reduce method. + + Example + ------- + >>> obj = ParamGen({True:1, False:0}) + >>> obj.reduce() + >>> obj.data + 1 + >>> obj.reset() + >>> obj.data + {True: 1, False: 0} + """ + self._data = deepcopy(self._original_data) + self._reduced = False + + def write_nml(self, output_path): + """Writes the reduced data in Fortran namelist format if the data conforms to the format. + + Parameters + ---------- + output_path: str object + Path to the namelist file to be created. + """ + + assert ( + self._reduced + ), "The data may be written only after the reduce method is called." + + # check *schema* after reduction + for grp, var in self.data.items(): + # grp is the namelist module name, while var is a dictionary corresponding to the vars in namelist + assert isinstance(grp, str), "Invalid data format" + assert isinstance(var, dict), "Invalid data format" + for vls in var.values(): + # vnm is the var name, and vls is its values element + assert isinstance(vls, dict), "Invalid data format" + for val in vls: + # val is a value in the values dict + assert isinstance(val, str), "Invalid data format" + + # write the namelist file + with open(output_path, "w") as nml: + for module in self._data: + nml.write("&{}\n".format(module)) + for var in self._data[module]: + val = str(self._data[module][var]["values"]).strip() + if val is not None: + nml.write(" {} = {}\n".format(var, val)) + nml.write("/\n\n") diff --git a/cime_config/ParamGen/paramgen_utils.py b/cime_config/ParamGen/paramgen_utils.py new file mode 100644 index 00000000..19e44fd5 --- /dev/null +++ b/cime_config/ParamGen/paramgen_utils.py @@ -0,0 +1,233 @@ +"""Auxiliary functions to be used in ParamGen and derived classes""" + +import re + + +def is_number(var): + """ + Returns True if the passed var (of type string) is a number. Returns + False if var is not a string or if it is not a number. + This function is an alternative to isnumeric(), which can't handle + scientific notation. + + Parameters + ---------- + var: str + variable to check whether number or not + Returns + ------- + True or False + + Example + ------- + >>> "1e-6".isnumeric() + False + >>> is_number("1e-6") and is_number(1) and is_number(3.14) + True + >>> is_number([1,2]) or is_number("hello") + False + """ + try: + float(var) + except ValueError: + return False + except TypeError: + return False + return True + + +def is_logical_expr(expr): + """ + Returns True if a string is a logical expression. + + Please note that fortran array syntax allows for + the use of parantheses and colons in namelist + variable names, which "eval" counts as a syntax error. + + Parameters + ---------- + expr: str + expression to check whether logical or not + Returns + ------- + True or False + + Example + ------- + >>> is_logical_expr("0 > 1000") + True + >>> is_logical_expr("3+4") + False + """ + + assert isinstance( + expr, str + ), "Expression passed to is_logical_expr function must be a string." + + # special case: + if expr.strip() == "else": + return True + + try: + return isinstance(eval(expr), bool) + except (NameError, SyntaxError): + return False + + +def is_formula(expr): + """ + Returns True if expr is a ParamGen formula to evaluate. This is determined by + checking whether expr is a string with a length of 1 or greater and if the + first character of expr is '='. + + Parameters + ---------- + expr: str + expression to check whether formula or not + Returns + ------- + True or False + + Example + ------- + >>> is_formula("3*5") + False + >>> is_formula("= 3*5") + True + """ + + return isinstance(expr, str) and len(expr) > 0 and expr.strip()[0] == "=" + + +def has_unexpanded_var(expr): + """ + Checks if a given expression has an expandable variable, e.g., $OCN_GRID, + that's not expanded yet. + + Parameters + ---------- + expr: str + expression to check + Returns + ------- + True or False + + Example + ------- + >>> has_unexpanded_var("${OCN_GRID} == tx0.66v1") + True + """ + + return isinstance(expr, str) and bool(re.search(r"(\$\w+|\${\w+\})", expr)) + + +def get_expandable_vars(expr): + """ + Returns the set of expandable vars from an expression. + + Parameters + ---------- + expr: str + expression to look for + Returns + ------- + a set of strings containing the expandable var names. + + Example + ------- + >>> get_expandable_vars("var1 $var2") + {'var2'} + >>> get_expandable_vars("var3 ${var4}") + {'var4'} + """ + expandable_vars = re.findall(r"(\$\w+|\${\w+\})", expr) + expandable_vars_stripped = set() + for var in expandable_vars: + var_stripped = var.strip().replace("$", "").replace("{", "").replace("}", "") + expandable_vars_stripped.add(var_stripped) + return expandable_vars_stripped + + +def _check_comparison_types(formula): + """ + A check to detect the comparison of different data types. This function + replaces equality comparisons with order comparisons to check whether + any variables of different types are compared. From Python 3.6 documentation: + A default order comparison (<, >, <=, and >=) is not provided; an attempt + raises TypeError. A motivation for this default behavior is the lack of a + similar invariant as for equality. + + Parameters + ---------- + formula: str + formula to check if it includes comparisons of different data types + Returns + ------- + True (or raises TypeError) + + Example + ------- + >>> _check_comparison_types("3.1 > 3") + True + >>> _check_comparison_types("'3.1' == 3.1") + Traceback (most recent call last): + ... + TypeError: The following formula may be comparing different types of variables: '3.1' == 3.1 + """ + guard_test = formula.replace("==", ">").replace("!=", ">").replace("<>", ">") + try: + eval(guard_test) + except TypeError as type_error: + raise TypeError( + "The following formula may be comparing different types of variables: {}".format( + formula + ) + ) from type_error + return True + + +def eval_formula(formula): + """ + This function evaluates a given formula and returns the result. It also + carries out several sanity checks before evaluation. + + Parameters + ---------- + formula: str + formula to evaluate + Returns + ------- + eval(formula) + + Example + ------- + >>> eval_formula("3*5") + 15 + >>> eval_formula("'tx0.66v1' != 'gx1v6'") + True + >>> eval_formula('$OCN_GRID != "gx1v6"') + Traceback (most recent call last): + ... + AssertionError + """ + + # make sure no expandable var exists in the formula. (They must already + # be expanded before this function is called.) + assert not has_unexpanded_var(formula) + + # Check whether any different data types are being compared + _check_comparison_types(formula) + + # now try to evaluate the formula: + try: + result = eval(formula) + except (TypeError, NameError, SyntaxError) as error: + raise RuntimeError("Cannot evaluate formula: " + formula) from error + + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/cime_config/ParamGen/xml_schema/entry_id_pg.xsd b/cime_config/ParamGen/xml_schema/entry_id_pg.xsd new file mode 100644 index 00000000..4142a179 --- /dev/null +++ b/cime_config/ParamGen/xml_schema/entry_id_pg.xsd @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cime_config/buildcpp b/cime_config/buildcpp index dc6cd8e8..8ea9436f 100644 --- a/cime_config/buildcpp +++ b/cime_config/buildcpp @@ -87,7 +87,6 @@ def buildcpp(case): hamocc_nattrc = case.get_value("HAMOCC_NATTRC") hamocc_sedbypass = case.get_value("HAMOCC_SEDBYPASS") hamocc_ciso = case.get_value("HAMOCC_CISO") - hamocc_vsls = case.get_value("HAMOCC_VSLS") blom_unit = case.get_value("BLOM_UNIT") pio_typename = case.get_value("PIO_TYPENAME", subgroup="OCN") @@ -143,7 +142,9 @@ def buildcpp(case): if hamocc_ciso: expect(hamocc_sedbypass, "HAMOCC C-isotopes currently not supported in the sediment module. Use HAMOCC_SEDBYPASS=TRUE") blom_cppdefs = blom_cppdefs + " -Dcisonew" - if hamocc_vsls: + if ocn_grid in ["tnx1v4"]: + # HAMOCC bromoform scheme currently only supported on the tnx1v4 grid + # (no swa-climatology has been created for other grid configurations)" blom_cppdefs = blom_cppdefs + " -DBROMO" if co2type == "prognostic": blom_cppdefs = blom_cppdefs + " -DPROGCO2" diff --git a/cime_config/buildnml b/cime_config/buildnml index ab3992b4..bf98558f 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -1,1677 +1,299 @@ -#! /bin/csh -f - -#------------------------------------------------------------------------------ -# Get variables from Case XML-files -#------------------------------------------------------------------------------ - -set CASEROOT = `./xmlquery CASEROOT --value` -set OCN_GRID = `./xmlquery OCN_GRID --value` -set BLOM_VCOORD = `./xmlquery BLOM_VCOORD --value` -set BLOM_UNIT = `./xmlquery BLOM_UNIT --value` -set DIN_LOC_ROOT = `./xmlquery DIN_LOC_ROOT --value` -set RUN_TYPE = `./xmlquery RUN_TYPE --value` -set CONTINUE_RUN = `./xmlquery CONTINUE_RUN --value` -set CASEBUILD = `./xmlquery CASEBUILD --value` -set CCSM_CO2_PPMV = `./xmlquery CCSM_CO2_PPMV --value` -set OCN_NCPL = `./xmlquery OCN_NCPL --value` -set BLOM_COUPLING = `./xmlquery BLOM_COUPLING --value` -set RUNDIR = `./xmlquery RUNDIR --value` -set BLOM_TRACER_MODULES = `./xmlquery BLOM_TRACER_MODULES --value` -set BLOM_RIVER_NUTRIENTS = `./xmlquery BLOM_RIVER_NUTRIENTS --value` -set BLOM_N_DEPOSITION = `./xmlquery BLOM_N_DEPOSITION --value` -set BLOM_NDEP_SCENARIO = `./xmlquery BLOM_NDEP_SCENARIO --value` -set HAMOCC_VSLS = `./xmlquery HAMOCC_VSLS --value` -set HAMOCC_CISO = `./xmlquery HAMOCC_CISO --value` -set HAMOCC_SEDSPINUP = `./xmlquery HAMOCC_SEDSPINUP --value` -set HAMOCC_SEDSPINUP_YR_START = `./xmlquery HAMOCC_SEDSPINUP_YR_START --value` -set HAMOCC_SEDSPINUP_YR_END = `./xmlquery HAMOCC_SEDSPINUP_YR_END --value` -set HAMOCC_SEDSPINUP_NCYCLE = `./xmlquery HAMOCC_SEDSPINUP_NCYCLE --value` -set RUN_STARTDATE = `./xmlquery RUN_STARTDATE --value` -set PIO_TYPENAME_OCN = `./xmlquery PIO_TYPENAME_OCN --value` -set PIO_NETCDF_FORMAT_OCN = `./xmlquery PIO_NETCDF_FORMAT_OCN --value` -set NINST_OCN = `./xmlquery NINST_OCN --value` -set TEST=`./xmlquery TEST --value` - -#------------------------------------------------------------------------------ -# Check if HAMOCC is requested -#------------------------------------------------------------------------------ - -set ecosys = FALSE -set tracers = (`echo $BLOM_TRACER_MODULES`) -if ($#tracers != 0) then - foreach module ($tracers) - if ($module == ecosys) then - set ecosys = TRUE - endif - end -endif - -#------------------------------------------------------------------------------ -# Set RUN_TYPE to 'continue' if CONTINUE_RUN equals TRUE -#------------------------------------------------------------------------------ - -if ("$CONTINUE_RUN" == TRUE) then - set RUN_TYPE = continue -endif - -#------------------------------------------------------------------------------ -# Get start date -#------------------------------------------------------------------------------ - -set YEAR0 = `echo $RUN_STARTDATE | cut -c1-4 ` -set MONTH0 = `echo $RUN_STARTDATE | cut -c6-7 ` -set DAY0 = `echo $RUN_STARTDATE | cut -c9-10` - -#------------------------------------------------------------------------------ -# Set namelist variables -#------------------------------------------------------------------------------ - -#------------------------------ -# set LIMITS defaults -#------------------------------ -set NDAY1 = 0 -set NDAY2 = 0 -set IDATE = $YEAR0$MONTH0$DAY0 -set IDATE0 = $YEAR0$MONTH0$DAY0 -set RUNID = "'unset'" -set EXPCNF = "'cesm'" -set RUNTYP = "'$RUN_TYPE'" -set GRFILE = "'unset'" -set ICFILE = "'unset'" -if ("$BLOM_UNIT" == cgs) then - set PREF = 2000.e5 -else - set PREF = 2000.e4 -endif -set BACLIN = 1800. -set BATROP = 36. -if ("$BLOM_UNIT" == cgs) then - set MDV2HI = 2. - set MDV2LO = .4 - set MDV4HI = 0. - set MDV4LO = 0. - set MDC2HI = 5000.e4 - set MDC2LO = 300.e4 -else - set MDV2HI = .02 - set MDV2LO = .004 - set MDV4HI = 0. - set MDV4LO = 0. - set MDC2HI = 5000. - set MDC2LO = 300. -endif -set VSC2HI = .5 -set VSC2LO = .5 -set VSC4HI = 0. -set VSC4LO = 0. -if ("$BLOM_UNIT" == cgs) then - set CBAR = 5. -else - set CBAR = .05 -endif -set CB = .002 -set CWBDTS = 5.e-5 -set CWBDLS = 25. -set MOMMTH = "'enscon'" -set BMCMTH = "'uc'" -set RMPMTH = "'eitvel'" -set MLRTTP = "'constant'" -set RM0 = 1.2 -set RM5 = 0. -set CE = .06 -set TDFILE = "'unset'" -set NIWGF = 0. -set NIWBF = .35 -set NIWLF = .5 -set SWAMTH = "'jerlov'" -set JWTYPE = 3 -set CHLOPT = "'climatology'" -set CCFILE = "'unset'" -set TRXDAY = 0. -set SRXDAY = 0. -set TRXDPT = 1. -set SRXDPT = 1. -set TRXLIM = 1.5 -set SRXLIM = .5 -set APTFLX = .false. -set APSFLX = .false. -set DITFLX = .false. -set DISFLX = .false. -set SRXBAL = .false. -set SCFILE = "'unset'" -set WAVSRC = "'none'" -set SMTFRC = .true. -set SPRFAC = .false. -set ATM_PATH = "'unset'" -set ITEST = 60 -set JTEST = 60 -set CNSVDI = .false. -set CSDIAG = .false. -set RSTFRQ = 1 -if ("$PIO_NETCDF_FORMAT_OCN" == 64bit_offset) then - set RSTFMT = 1 -else - set RSTFMT = 0 -endif -set RSTCMP = 0 -if ("$PIO_TYPENAME_OCN" == pnetcdf) then - set IOTYPE = 1 -else - set IOTYPE = 0 -endif - -#------------------------------ -# set VCOORD defaults -#------------------------------ -set VCOORD_TYPE = "'$BLOM_VCOORD'" -set RECONSTRUCTION_METHOD = "'ppm'" -set DENSITY_LIMITING = "'monotonic'" -set TRACER_LIMITING = "'non_oscillatory'" -set VELOCITY_LIMITING = "'non_oscillatory'" -set DENSITY_PC_UPPER_BNDR = .false. -set DENSITY_PC_LOWER_BNDR = .false. -set TRACER_PC_UPPER_BNDR = .true. -set TRACER_PC_LOWER_BNDR = .false. -set VELOCITY_PC_UPPER_BNDR = .true. -set VELOCITY_PC_LOWER_BNDR = .false. -set DPMIN_SURFACE = 2.5 -set DPMIN_INFLATION_FACTOR = 1.08 -set DPMIN_INTERIOR = .1 -set DKTZU = 4 -set DKTZL = 1 - -#------------------------------ -# set DIFFUSION defaults -#------------------------------ -set EITMTH = "'gm'" -set EDRITP = "'large scale'" -set EDWMTH = "'smooth'" -set EDDF2D = .false. -set EDSPRS = .true. -set EGC = 0.85 -set EGGAM = 200. -if ("$BLOM_UNIT" == cgs) then - set EGLSMN = 4000.e2 - set EGMNDF = 100.e4 - set EGMXDF = 1500.e4 -else - set EGLSMN = 4000. - set EGMNDF = 100. - set EGMXDF = 1500. -endif -set EGIDFQ = 1. -set TBFILE = "'unset'" -set RHISCF = 0. -set EDANIS = .false. -set REDI3D = .false. -set RHSCTP = .false. -set RI0 = 1.2 -set BDMTYP = 2 -if ("$BLOM_UNIT" == cgs) then - set BDMC1 = 5.e-4 - set BDMC2 = .1 -else - set BDMC1 = 5.e-8 - set BDMC2 = 1.e-5 -endif -set TKEPF = .006 -set SMOBLD = .true. -set LNGMTP = "'none'" -if ("$BLOM_VCOORD" == isopyc_bulkml) then - set BDMLDP = .true. - set LTEDTP = "'layer'" -else - set BDMLDP = .false. - set LTEDTP = "'neutral'" -endif - -#------------------------------ -# set BGCNML defaults -#------------------------------ -set ATM_CO2 = "$CCSM_CO2_PPMV" -if ("$BLOM_RIVER_NUTRIENTS" == TRUE) then - set DO_RIVINPT = .true. -else - set DO_RIVINPT = .false. -endif -if ("$BLOM_N_DEPOSITION" == TRUE) then - set DO_NDEP = .true. - if( "$BLOM_NDEP_SCENARIO" == 1850 && "$OCN_GRID" == tnx2v1) then - set NDEPFNAME = ndep_1850_CMIP6_tnx2v1_20180321.nc - else if( "$BLOM_NDEP_SCENARIO" == 1850 && "$OCN_GRID" == tnx1v4) then - set NDEPFNAME = ndep_1850_CMIP6_tnx1v4_20171106.nc - else if( "$BLOM_NDEP_SCENARIO" == 1850 && "$OCN_GRID" == tnx0.25v4) then - set NDEPFNAME = ndep_1850_CMIP6_tnx0.25v4_20190912.nc - else if( "$BLOM_NDEP_SCENARIO" == 1850 && "$OCN_GRID" == tnx0.125v4) then - set NDEPFNAME = ndep_1850_CMIP6_tnx0.125v4_20221013.nc - else if( "$BLOM_NDEP_SCENARIO" == 2000 && "$OCN_GRID" == tnx2v1) then - set NDEPFNAME = ndep_2000_CMIP6_tnx2v1_20200826.nc - else if( "$BLOM_NDEP_SCENARIO" == 2000 && "$OCN_GRID" == tnx1v4) then - set NDEPFNAME = ndep_2000_CMIP6_tnx1v4_20200826.nc - else if( "$BLOM_NDEP_SCENARIO" == 2000 && "$OCN_GRID" == tnx0.25v4) then - set NDEPFNAME = ndep_2000_CMIP6_tnx0.25v4_20200826.nc - else if( "$BLOM_NDEP_SCENARIO" == hist && "$OCN_GRID" == tnx2v1) then - set NDEPFNAME = ndep_185001-201412_tnx2v1_20190702.nc - else if( "$BLOM_NDEP_SCENARIO" == hist && "$OCN_GRID" == tnx1v4) then - set NDEPFNAME = ndep_185001-201412_tnx1v4_20180613.nc - else if( "$BLOM_NDEP_SCENARIO" == hist && "$OCN_GRID" == tnx0.25v4) then - set NDEPFNAME = ndep_185001-201412_tnx0.25v4_20190705.nc - else if( `echo $BLOM_NDEP_SCENARIO | cut -c1-3` == ssp && "$OCN_GRID" == tnx1v4) then - set NDEPFNAME = ndep_201501-210012-${BLOM_NDEP_SCENARIO}_tnx1v4_20191112.nc - else if( "$BLOM_NDEP_SCENARIO" == UNSET ) then - set DO_NDEP = .false. - set NDEPFNAME = "''" - endif -else - set DO_NDEP = .false. - set NDEPFNAME = "''" -endif -if ("$HAMOCC_SEDSPINUP" == TRUE) then - set DO_SEDSPINUP = .true. - set SEDSPIN_YR_S = $HAMOCC_SEDSPINUP_YR_START - set SEDSPIN_YR_E = $HAMOCC_SEDSPINUP_YR_END - set SEDSPIN_NCYC = $HAMOCC_SEDSPINUP_NCYCLE -else - set DO_SEDSPINUP = .false. - set SEDSPIN_YR_S = -1 - set SEDSPIN_YR_E = -1 - set SEDSPIN_NCYC = -1 -endif -# VSLS is currently only available for the tnx1v4 grid, since a climatlogy file for -# short wave radiation is needed. -if ("$HAMOCC_VSLS" == TRUE && "$OCN_GRID" != tnx1v4) then - echo "$0 ERROR: HAMOCC_VSLS == TRUE not possible with this grid resolution (no swa-climatology available) " - exit -1 -endif -# For the following options, there are currently no switches in Case-XML files. -# These options can be activated by expert users via user namelist. -set DO_OALK = .false. -set BGCOAFX_OALKSCEN = "''" -set BGCOAFX_OALKFILE = "''" -set BGCOAFX_ADDALK = 0.135 -set BGCOAFX_CDRMIP_LATMAX = 70.0 -set BGCOAFX_CDRMIP_LATMIN = -60.0 -set BGCOAFX_RAMP_START = 2025 -set BGCOAFX_RAMP_END = 2035 -set WITH_DMSPH = .false. -set PI_PH_FILE = "''" -set L_3DVARSEDPOR = .false. -set SEDPORFILE = "''" - - -#------------------------------ -# set DIAPHY defaults -#------------------------------ -set GLB_FNAMETAG = "'hd','hm','hy'" -set GLB_AVEPERIO = '1, 30, 365' -if ("$TEST" == TRUE) then - set GLB_FILEFREQ = '1, 30, 365' -else - set GLB_FILEFREQ = '30, 30, 365' -endif -set GLB_COMPFLAG = '0, 0, 0' -if ("$PIO_NETCDF_FORMAT_OCN" == 64bit_offset) then - set GLB_NCFORMAT = '1, 1, 1' -else - set GLB_NCFORMAT = '0, 0, 0' -endif -set H2D_ABSWND = '0, 4, 0' - -set H2D_ALB = '0, 0, 0' - -set H2D_BTMSTR = '0, 4, 0' -set H2D_BRNFLX = '0, 4, 0' -set H2D_BRNPD = '0, 4, 0' - -set H2D_DFL = '0, 0, 0' - -set H2D_EVA = '0, 4, 0' -set H2D_FICE = '0, 4, 0' -set H2D_FMLTFZ = '0, 4, 0' - -set H2D_HICE = '0, 0, 0' - -set H2D_HMLTFZ = '0, 4, 0' - -set H2D_HSNW = '0, 0, 0' -set H2D_IAGE = '0, 0, 0' - -set H2D_IDKEDT = '0, 4, 0' -set H2D_LAMULT = '0, 4, 0' -set H2D_LASL = '0, 4, 0' -set H2D_LIP = '0, 4, 0' - -set H2D_MAXMLD = '4, 4, 0' - -set H2D_MLD = '0, 4, 0' - -set H2D_MLTS = '4, 4, 0' - -set H2D_MLTSMN = '0, 4, 0' -set H2D_MLTSMX = '0, 4, 0' -set H2D_MLTSSQ = '0, 4, 0' -set H2D_MTKEUS = '0, 4, 0' -set H2D_MTKENI = '0, 4, 0' -set H2D_MTKEBF = '0, 4, 0' -set H2D_MTKERS = '0, 4, 0' -set H2D_MTKEPE = '0, 4, 0' -set H2D_MTKEKE = '0, 4, 0' -set H2D_MTY = '0, 4, 0' -set H2D_NSF = '0, 4, 0' -set H2D_PBOT = '0, 4, 0' -set H2D_PSRF = '0, 4, 0' -set H2D_RFIFLX = '0, 4, 0' -set H2D_RNFFLX = '0, 4, 0' -set H2D_SALFLX = '0, 4, 0' -set H2D_SALRLX = '0, 4, 0' -set H2D_SBOT = '0, 4, 0' - -set H2D_SEALV = '4, 4, 0' - -set H2D_SLVSQ = '0, 4, 0' -set H2D_SFL = '0, 4, 0' -set H2D_SOP = '0, 4, 0' -set H2D_SIGMX = '0, 4, 0' - -set H2D_SSS = '4, 4, 0' -set H2D_SSSSQ = '4, 4, 0' -set H2D_SST = '4, 4, 0' -set H2D_SSTSQ = '4, 4, 0' - -set H2D_SURFLX = '0, 4, 0' -set H2D_SURRLX = '0, 4, 0' -set H2D_SWA = '0, 4, 0' - -set H2D_T20D = '4, 4, 0' - -set H2D_TAUX = '0, 4, 0' -set H2D_TAUY = '0, 4, 0' -set H2D_TBOT = '0, 4, 0' - -set H2D_TICE = '0, 0, 0' -set H2D_TSRF = '0, 0, 0' - -set H2D_UB = '0, 4, 0' - -set H2D_UICE = '0, 0, 0' - -set H2D_USTAR = '0, 4, 0' -set H2D_USTAR3 = '0, 4, 0' - -set H2D_USTOKES = '0, 0, 0' - -set H2D_VB = '0, 4, 0' - -set H2D_VICE = '0, 0, 0' -set H2D_VSTOKES = '0, 0, 0' - -set H2D_ZTX = '0, 4, 0' -set LYR_BFSQ = '0, 4, 0' -set LYR_DIFDIA = '0, 4, 0' -set LYR_DIFVMO = '0, 4, 0' -set LYR_DIFVHO = '0, 4, 0' -set LYR_DIFVSO = '0, 4, 0' -set LYR_DIFINT = '0, 4, 0' -set LYR_DIFISO = '0, 4, 0' -set LYR_DP = '0, 4, 0' -set LYR_DZ = '0, 4, 0' -set LYR_SALN = '0, 4, 0' -set LYR_TEMP = '0, 4, 0' - -set LYR_TRC = '0, 0, 0' - -set LYR_UFLX = '0, 4, 0' -set LYR_UTFLX = '0, 4, 0' -set LYR_USFLX = '0, 4, 0' - -set LYR_UMFLTD = '0, 0, 4' -set LYR_UMFLSM = '0, 0, 4' -set LYR_UTFLTD = '0, 0, 4' -set LYR_UTFLSM = '0, 0, 4' -set LYR_UTFLLD = '0, 0, 4' -set LYR_USFLTD = '0, 0, 4' -set LYR_USFLSM = '0, 0, 4' -set LYR_USFLLD = '0, 0, 4' - -set LYR_UVEL = '0, 4, 0' -set LYR_VFLX = '0, 4, 0' -set LYR_VTFLX = '0, 4, 0' -set LYR_VSFLX = '0, 4, 0' - -set LYR_VMFLTD = '0, 0, 4' -set LYR_VMFLSM = '0, 0, 4' -set LYR_VTFLTD = '0, 0, 4' -set LYR_VTFLSM = '0, 0, 4' -set LYR_VTFLLD = '0, 0, 4' -set LYR_VSFLTD = '0, 0, 4' -set LYR_VSFLSM = '0, 0, 4' -set LYR_VSFLLD = '0, 0, 4' - -set LYR_VVEL = '0, 4, 0' -set LYR_WFLX = '0, 4, 0' -set LYR_WFLX2 = '0, 4, 0' -set LYR_PV = '0, 4, 0' -set LYR_TKE = '0, 4, 0' -set LYR_GLS_PSI = '0, 4, 0' -set LYR_IDLAGE = '0, 4, 0' -set LVL_BFSQ = '0, 4, 0' -set LVL_DIFDIA = '0, 4, 0' -set LVL_DIFVMO = '0, 4, 0' -set LVL_DIFVHO = '0, 4, 0' -set LVL_DIFVSO = '0, 4, 0' -set LVL_DIFINT = '0, 4, 0' -set LVL_DIFISO = '0, 4, 0' -set LVL_DZ = '0, 4, 0' -set LVL_SALN = '0, 4, 0' -set LVL_TEMP = '0, 4, 0' - -set LVL_TRC = '0, 0, 0' - -set LVL_UFLX = '0, 4, 0' -set LVL_UTFLX = '0, 4, 0' -set LVL_USFLX = '0, 4, 0' - -set LVL_UMFLTD = '0, 0, 4' -set LVL_UMFLSM = '0, 0, 4' -set LVL_UTFLTD = '0, 0, 4' -set LVL_UTFLSM = '0, 0, 4' -set LVL_UTFLLD = '0, 0, 4' -set LVL_USFLTD = '0, 0, 4' -set LVL_USFLSM = '0, 0, 4' -set LVL_USFLLD = '0, 0, 4' - -set LVL_UVEL = '0, 4, 0' -set LVL_VFLX = '0, 4, 0' -set LVL_VTFLX = '0, 4, 0' -set LVL_VSFLX = '0, 4, 0' - -set LVL_VMFLTD = '0, 0, 4' -set LVL_VMFLSM = '0, 0, 4' -set LVL_VTFLTD = '0, 0, 4' -set LVL_VTFLSM = '0, 0, 4' -set LVL_VTFLLD = '0, 0, 4' -set LVL_VSFLTD = '0, 0, 4' -set LVL_VSFLSM = '0, 0, 4' -set LVL_VSFLLD = '0, 0, 4' - -set LVL_VVEL = '0, 4, 0' -set LVL_WFLX = '0, 4, 0' -set LVL_WFLX2 = '0, 4, 0' -set LVL_PV = '0, 4, 0' -set LVL_TKE = '0, 4, 0' -set LVL_GLS_PSI = '0, 4, 0' -set LVL_IDLAGE = '0, 4, 0' -set MSC_MMFLXL = '0, 4, 0' -set MSC_MMFLXD = '0, 4, 0' -set MSC_MMFTDL = '0, 4, 0' -set MSC_MMFSML = '0, 4, 0' -set MSC_MMFTDD = '0, 4, 0' -set MSC_MMFSMD = '0, 4, 0' -set MSC_MHFLX = '0, 4, 0' -set MSC_MHFTD = '0, 4, 0' -set MSC_MHFSM = '0, 4, 0' -set MSC_MHFLD = '0, 4, 0' -set MSC_MSFLX = '0, 4, 0' -set MSC_MSFTD = '0, 4, 0' -set MSC_MSFSM = '0, 4, 0' -set MSC_MSFLD = '0, 4, 0' -set MSC_VOLTR = '0, 4, 0' -set MSC_MASSGS = '0, 4, 0' -set MSC_VOLGS = '0, 4, 0' -set MSC_SALNGA = '0, 4, 0' -set MSC_TEMPGA = '0, 4, 0' -set MSC_SSSGA = '0, 4, 0' -set MSC_SSTGA = '0, 4, 0' - -#------------------------------ -# set DIABGC defaults -#------------------------------ -set BGC_FNAMETAG = "'hbgcd','hbgcm','hbgcy'" -set BGC_AVEPERIO = '1,30,365' -set BGC_FILEFREQ = '30,30,365' -if ("$TEST" == TRUE) then - set BGC_FILEFREQ = ' 1,30,365' -else - set BGC_FILEFREQ = '30,30,365' -endif -set BGC_COMPFLAG = '0, 0, 0' -if ("$PIO_NETCDF_FORMAT_OCN" == 64bit_offset) then - set BGC_NCFORMAT = '1, 1, 1' -else - set BGC_NCFORMAT = '0, 0, 0' -endif -set BGC_INVENTORY = '0, 1, 0' -set SRF_PHOSPH = '0, 2, 2' -set SRF_OXYGEN = '0, 2, 2' -set SRF_IRON = '0, 2, 2' -set SRF_ANO3 = '0, 2, 2' -set SRF_ALKALI = '4, 2, 2' -set SRF_SILICA = '0, 2, 2' -set SRF_DIC = '4, 2, 2' -set SRF_PHYTO = '4, 2, 2' -set SRF_PH = '0, 2, 2' -set SRF_EXPORT = '0, 2, 2' -set SRF_EXPOSI = '0, 2, 2' -set SRF_EXPOCA = '0, 2, 2' -set SRF_KWCO2 = '0, 2, 2' -set SRF_KWCO2KHM = '0, 2, 2' -set SRF_CO2KH = '0, 2, 2' -set SRF_CO2KHM = '0, 2, 2' -set SRF_PCO2 = '0, 2, 2' -set SRF_PCO2M = '0, 2, 2' -set SRF_CO2FXD = '4, 2, 2' -set SRF_CO2FXU = '4, 2, 2' -set SRF_OXFLUX = '0, 2, 2' -set SRF_NIFLUX = '0, 2, 2' -set SRF_N2OFX = '0, 0, 2' -set SRF_DMSFLUX = '0, 2, 2' -set SRF_DMS = '0, 2, 2' -set SRF_DMSPROD = '0, 2, 2' -set SRF_DMS_BAC = '0, 2, 2' -set SRF_DMS_UV = '0, 2, 2' -set SRF_ATMCO2 = '0, 2, 2' -set SRF_ATMO2 = '0, 2, 2' -set SRF_ATMN2 = '0, 2, 2' -set SRF_NATDIC = '0, 2, 2' -set SRF_NATALKALI = '0, 2, 2' -set SRF_NATPH = '0, 2, 2' -set SRF_NATPCO2 = '0, 2, 2' -set SRF_NATCO2FX = '0, 2, 2' -set SRF_CO213FXD = '0, 2, 2' -set SRF_CO213FXU = '0, 2, 2' -set SRF_CO214FXD = '0, 2, 2' -set SRF_CO214FXU = '0, 2, 2' -set SRF_CFC11 = '0, 2, 2' -set SRF_CFC12 = '0, 2, 2' -set SRF_SF6 = '0, 2, 2' -set SRF_BROMO = '0, 2, 2' -set SRF_BROMOFX = '0, 2, 2' -set INT_BROMOPRO = '0, 2, 2' -set INT_BROMOUV = '0, 2, 2' -set INT_PHOSY = '4, 2, 2' -set INT_NFIX = '0, 2, 2' -set INT_DNIT = '0, 2, 2' -if ("$BLOM_N_DEPOSITION" == TRUE) then -set FLX_NDEP = '0, 2, 2' -else -set FLX_NDEP = '0, 0, 0' -endif -set FLX_OALK = '0, 0, 0' -set FLX_CAR0100 = '0, 2, 2' -set FLX_CAR0500 = '0, 2, 2' -set FLX_CAR1000 = '0, 2, 2' -set FLX_CAR2000 = '0, 2, 2' -set FLX_CAR4000 = '0, 2, 2' -set FLX_CAR_BOT = '0, 2, 2' -set FLX_BSI0100 = '0, 2, 2' -set FLX_BSI0500 = '0, 2, 2' -set FLX_BSI1000 = '0, 2, 2' -set FLX_BSI2000 = '0, 2, 2' -set FLX_BSI4000 = '0, 2, 2' -set FLX_BSI_BOT = '0, 2, 2' -set FLX_CAL0100 = '0, 2, 2' -set FLX_CAL0500 = '0, 2, 2' -set FLX_CAL1000 = '0, 2, 2' -set FLX_CAL2000 = '0, 2, 2' -set FLX_CAL4000 = '0, 2, 2' -set FLX_CAL_BOT = '0, 2, 2' -set LYR_PHYTO = '0, 0, 2' -set LYR_GRAZER = '0, 0, 2' -set LYR_DOC = '0, 0, 2' -set LYR_PHOSY = '0, 0, 2' -set LYR_PHOSPH = '0, 0, 2' -set LYR_OXYGEN = '0, 0, 4' -set LYR_IRON = '0, 0, 2' -set LYR_ANO3 = '0, 0, 2' -set LYR_ALKALI = '0, 0, 2' -set LYR_SILICA = '0, 0, 2' -set LYR_DIC = '0, 0, 2' -set LYR_POC = '0, 0, 2' -set LYR_CALC = '0, 0, 2' -set LYR_OPAL = '0, 0, 2' -set LYR_CO3 = '0, 0, 2' -set LYR_N2O = '0, 0, 0' -set LYR_PH = '0, 0, 2' -set LYR_OMEGAC = '0, 0, 2' -set LYR_OMEGAA = '0, 0, 2' -set LYR_PREFO2 = '0, 0, 4' -set LYR_O2SAT = '0, 0, 4' -set LYR_PREFPO4 = '0, 0, 2' -set LYR_PREFALK = '0, 0, 2' -set LYR_PREFDIC = '0, 0, 2' -set LYR_DICSAT = '0, 0, 2' -set LYR_NATDIC = '0, 0, 2' -set LYR_NATALKALI = '0, 0, 2' -set LYR_NATCO3 = '0, 0, 2' -set LYR_NATCALC = '0, 0, 2' -set LYR_NATPH = '0, 0, 2' -set LYR_NATOMEGAC = '0, 0, 2' -set LYR_NATOMEGAA = '0, 0, 2' -set LYR_DIC13 = '0, 0, 2' -set LYR_DIC14 = '0, 0, 2' -set LYR_D13C = '0, 0, 2' -set LYR_D14C = '0, 0, 2' -set LYR_BIGD14C = '0, 0, 2' -set LYR_POC13 = '0, 0, 2' -set LYR_DOC13 = '0, 0, 2' -set LYR_CALC13 = '0, 0, 2' -set LYR_PHYTO13 = '0, 0, 2' -set LYR_GRAZER13 = '0, 0, 2' -set LYR_CFC11 = '0, 0, 2' -set LYR_CFC12 = '0, 0, 2' -set LYR_SF6 = '0, 0, 2' -set LYR_NOS = '0, 0, 2' -set LYR_WPHY = '0, 0, 2' -set LYR_WNOS = '0, 0, 2' -set LYR_EPS = '0, 0, 0' -set LYR_ASIZE = '0, 0, 0' -set LYR_BROMO = '0, 0, 2' -set BGC_DP = '0, 2, 2' -set LVL_PHYTO = '0, 2, 2' -set LVL_GRAZER = '0, 2, 2' -set LVL_DOC = '0, 2, 2' -set LVL_PHOSY = '0, 2, 2' -set LVL_PHOSPH = '0, 2, 2' -set LVL_OXYGEN = '0, 4, 4' -set LVL_IRON = '0, 2, 2' -set LVL_ANO3 = '0, 2, 2' -set LVL_ALKALI = '0, 2, 2' -set LVL_SILICA = '0, 2, 2' -set LVL_DIC = '0, 2, 2' -set LVL_POC = '0, 2, 2' -set LVL_CALC = '0, 2, 2' -set LVL_OPAL = '0, 2, 2' -set LVL_CO3 = '0, 2, 2' -set LVL_N2O = '0, 0, 2' -set LVL_PH = '0, 2, 2' -set LVL_OMEGAC = '0, 2, 2' -set LVL_OMEGAA = '0, 2, 2' -set LVL_PREFO2 = '0, 4, 4' -set LVL_O2SAT = '0, 4, 4' -set LVL_PREFPO4 = '0, 2, 2' -set LVL_PREFALK = '0, 2, 2' -set LVL_PREFDIC = '0, 2, 2' -set LVL_DICSAT = '0, 2, 2' -set LVL_NATDIC = '0, 2, 2' -set LVL_NATALKALI = '0, 2, 2' -set LVL_NATCO3 = '0, 2, 2' -set LVL_NATCALC = '0, 2, 2' -set LVL_NATPH = '0, 2, 2' -set LVL_NATOMEGAC = '0, 2, 2' -set LVL_NATOMEGAA = '0, 2, 2' -set LVL_DIC13 = '0, 2, 2' -set LVL_DIC14 = '0, 2, 2' -set LVL_D13C = '0, 2, 2' -set LVL_POC13 = '0, 2, 2' -set LVL_DOC13 = '0, 2, 2' -set LVL_CALC13 = '0, 2, 2' -set LVL_PHYTO13 = '0, 2, 2' -set LVL_GRAZER13 = '0, 2, 2' -set LVL_CFC11 = '0, 2, 2' -set LVL_CFC12 = '0, 2, 2' -set LVL_SF6 = '0, 2, 2' -set LVL_NOS = '0, 2, 2' -set LVL_WPHY = '0, 2, 2' -set LVL_WNOS = '0, 2, 2' -set LVL_EPS = '0, 0, 0' -set LVL_ASIZE = '0, 0, 0' -set LVL_BROMO = '0, 2, 2' -set FLX_SEDIFFIC = '0, 0, 2' -set FLX_SEDIFFAL = '0, 0, 2' -set FLX_SEDIFFPH = '0, 0, 2' -set FLX_SEDIFFOX = '0, 0, 2' -set FLX_SEDIFFN2 = '0, 0, 2' -set FLX_SEDIFFNO3 = '0, 0, 2' -set FLX_SEDIFFSI = '0, 0, 2' -set SDM_POWAIC = '0, 0, 2' -set SDM_POWAAL = '0, 0, 2' -set SDM_POWAPH = '0, 0, 2' -set SDM_POWAOX = '0, 0, 2' -set SDM_POWN2 = '0, 0, 2' -set SDM_POWNO3 = '0, 0, 2' -set SDM_POWASI = '0, 0, 2' -set SDM_SSSO12 = '0, 0, 2' -set SDM_SSSSIL = '0, 0, 2' -set SDM_SSSC12 = '0, 0, 2' -set SDM_SSSTER = '0, 0, 2' -set BUR_SSSO12 = '0, 0, 2' -set BUR_SSSSIL = '0, 0, 2' -set BUR_SSSC12 = '0, 0, 2' -set BUR_SSSTER = '0, 0, 2' - -# if partial coupling, enable SSS relaxation -if ("$BLOM_COUPLING" =~ *partial*) then - if ("$BLOM_VCOORD" == isopyc_bulkml) then - set SRXDAY = 6. - else - set SRXDAY = 60. - set SRXDPT = 10. - endif - set SPRFAC = .true. - set SRXBAL = .true. -endif - -# set grid dependent parameters -if ("$OCN_GRID" == gx1v5 || "$OCN_GRID" == gx1v6) then - set BACLIN = 1800. - set BATROP = 36. -else if ("$OCN_GRID" == gx3v7) then - set BACLIN = 3600. - set BATROP = 72. -else if ("$OCN_GRID" == tnx2v1 ) then - set BACLIN = 4800. - set BATROP = 96. - set EGC = 0.5 - if ("$BLOM_UNIT" == cgs) then - set EGMXDF = 1000.e4 - else - set EGMXDF = 1000. - endif - set CWMTAG = "'Gibraltar','Gibraltar'" - set CWMEDG = " 'u', 'u'" - set CWMI = " 53, 54" - set CWMJ = " 137, 137" - set CWMWTH = " 30.e3, 30.e3" -else if ("$OCN_GRID" == tnx1.5v1 ) then - set BACLIN = 4800. - set BATROP = 96. - set EGC = 0.5 - if ("$BLOM_UNIT" == cgs) then - set EGMXDF = 1000.e4 - else - set EGMXDF = 1000. - endif -else if ("$OCN_GRID" == tnx1v1 || "$OCN_GRID" == tnx1v3 || "$OCN_GRID" == tnx1v4) then - if ("$OCN_NCPL" == 24) then - set BACLIN = 3600. - set BATROP = 60. - set CWBDTS = .75e-4 - set NIWGF = .4 - set SMTFRC = .false. - if ("$BLOM_VCOORD" == isopyc_bulkml) then - set CE = .5 - endif - else - set BACLIN = 3200. - set BATROP = 64. - endif - set CWMTAG = "'Gibraltar','Gibraltar'" - set CWMEDG = " 'u', 'u'" - set CWMI = " 105, 106" - set CWMJ = " 273, 273" - set CWMWTH = " 30.e3, 30.e3" -else if ("$OCN_GRID" == tnx0.25v1 || "$OCN_GRID" == tnx0.25v3 || "$OCN_GRID" == tnx0.25v4) then - set BACLIN = 900. - set BATROP = 15. - if ("$BLOM_UNIT" == cgs) then - set MDV2HI = .15 - set MDV2LO = .15 - set MDV4HI = 0. - set MDV4LO = 0. - set MDC2HI = 300.e4 - set MDC2LO = 300.e4 - else - set MDV2HI = .0015 - set MDV2LO = .0015 - set MDV4HI = 0. - set MDV4LO = 0. - set MDC2HI = 300. - set MDC2LO = 300. - endif - set VSC2HI = .15 - set VSC2LO = .15 - set VSC4HI = 0.0625 - set VSC4LO = 0.0625 - set CWBDTS = 0.75e-4 - set CWBDLS = 25. - set EDWMTH = "'step'" - set EGC = 0.85 - if ("$BLOM_UNIT" == cgs) then - set EGMXDF = 1500.e4 - else - set EGMXDF = 1500. - endif - if ("$BLOM_VCOORD" == isopyc_bulkml) then - set CE = 1.0 - endif -else if ("$OCN_GRID" == tnx0.125v4) then - set BACLIN = 300. - set BATROP = 6. - set EGMNDF = 0. - set EGMXDF = 0. - set EDWMTH = "'step'" - set CWBDTS = .75e-4 - set CWBDLS = 25 - if ("$BLOM_UNIT" == cgs) then - set MDV2HI = .1 - set MDV2LO = .1 - set MDV4HI = 0. - set MDV4LO = 0. - set MDC2HI = 300.e4 - set MDC2LO = 100.e4 - else - set MDV2HI = .001 - set MDV2LO = .001 - set MDV4HI = 0. - set MDV4LO = 0. - set MDC2HI = 300. - set MDC2LO = 100. - endif - set VSC2HI = 0. - set VSC2LO = 0. - set VSC4HI = .06 - set VSC4LO = .06 - set LTEDTP = "'layer'" -else - echo "OCN_GRID is $OCN_GRID \n" - echo "$0 ERROR: Cannot deal with GRID equal to $OCN_GRID " - exit -1 -endif - -# set grid independent input files (iHAMOCC initial conditions) -set INIDIC = "'$DIN_LOC_ROOT/ocn/blom/inicon/glodapv2_Ct_preind_OMIPinit_20171107.nc'" -set INIALK = "'$DIN_LOC_ROOT/ocn/blom/inicon/glodapv2_At_OMIPinit_20171107.nc'" -set INIPO4 = "'$DIN_LOC_ROOT/ocn/blom/inicon/woa13_phosphate_OMIPinit_20171107.nc'" -set INIOXY = "'$DIN_LOC_ROOT/ocn/blom/inicon/woa13_oxygen_OMIPinit_20171107.nc'" -set ININO3 = "'$DIN_LOC_ROOT/ocn/blom/inicon/woa13_nitrate_OMIPinit_20171107.nc'" -set INISIL = "'$DIN_LOC_ROOT/ocn/blom/inicon/woa13_silicate_OMIPinit_20171107.nc'" -if ("$HAMOCC_CISO" == TRUE) then -set INID13C = "'$DIN_LOC_ROOT/ocn/blom/inicon/d13C_permil_20180609.nc'" -set INID14C = "'$DIN_LOC_ROOT/ocn/blom/inicon/d14C_permil_20180609.nc'" -else -set INID13C = "''" -set INID14C = "''" -endif - -# set grid dependent input files -if ("$OCN_GRID" == tnx2v1) then - set GRFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/grid_tnx2v1_20130206.nc'" - set ICFILE = "'$DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx2v1_20130419.nc'" - set TDFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx2v1_20130419.nc'" - set MER_ORFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx2v1_20190826.nc'" - set MER_MIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx2v1_20190826.dat'" - set SEC_SIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx2v1_20190826.dat'" - set SCFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx2v1_20130927.nc'" - set FEDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx2v1_20130506.nc'" - set SWACLIMFILE = "''" - set SEDPORFILE = "''" - if ("$BLOM_RIVER_NUTRIENTS" == TRUE) then - set RIVINFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx2v1_20170915.nc'" - else - set RIVINFILE = "''" - endif - if ("$BLOM_N_DEPOSITION" == TRUE) then - set NDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/$NDEPFNAME'" - else - set NDEPFILE = "''" - endif -else if ("$OCN_GRID" == tnx1v4) then - set GRFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/grid_tnx1v4_20170622.nc'" - set ICFILE = "'$DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx1v4_20170622.nc'" - set TDFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx1v4_20170605.nc'" - set MER_ORFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx1v4_20190729.nc'" - set MER_MIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx1v4_20190615.dat'" - set SEC_SIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx1v4_20190611.dat'" - set CCFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/chlorophyll_concentration_tnx1v4_20170608.nc'" - set SCFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx1v4_20170604.nc'" - set FEDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx1v4_20171107.nc'" - set SEDPORFILE = "''" - if ("$HAMOCC_VSLS" == TRUE) then - set SWACLIMFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/Annual_clim_swa_tnx1v4_20210415.nc'" - else - set SWACLIMFILE = "''" - endif - if ("$BLOM_RIVER_NUTRIENTS" == TRUE) then - set RIVINFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx1v4_20170820.nc'" - else - set RIVINFILE = "''" - endif - if ("$BLOM_N_DEPOSITION" == TRUE) then - set NDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/$NDEPFNAME'" - else - set NDEPFILE = "''" - endif -else if ("$OCN_GRID" == tnx0.25v4) then - set GRFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/grid_tnx0.25v4_20170622.nc'" - set ICFILE = "'$DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx0.25v4_20170623.nc'" - set TDFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx0.25v4_20170626.nc'" - set MER_ORFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx0.25v4_20190612.nc'" - set MER_MIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx0.25v4_20190701.dat'" - set SEC_SIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx0.25v4_20190612.dat'" - set CCFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/chlorophyll_concentration_tnx0.25v4_20170623.nc'" - set SCFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx0.25v4_20170623.nc'" - set FEDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx0.25v4_20181004.nc'" - set SWACLIMFILE = "''" - set SEDPORFILE = "''" - if ("$BLOM_RIVER_NUTRIENTS" == TRUE) then - set RIVINFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx0.25v4_20170821.nc'" - else - set RIVINFILE = "''" - endif - if ("$BLOM_N_DEPOSITION" == TRUE) then - set NDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/$NDEPFNAME'" - else - set NDEPFILE = "''" - endif -else if ("$OCN_GRID" == tnx0.125v4) then - set GRFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/grid_tnx0.125v4_20221013.nc'" - set ICFILE = "'$DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx0.125v4_20230318.nc'" - set TDFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx0.125v4_20221013.nc'" - set MER_ORFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx0.125v4_20221013.nc'" - set MER_MIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx0.125v4_20221013.dat'" - set SEC_SIFILE = "'$DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx0.125v4_20221013.dat'" - set CCFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/chlorophyll_concentration_tnx0.125v4_20221013.nc'" - set SCFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx0.125v4_20221013.nc'" - set FEDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx0.125v4_20221013.nc'" - set SWACLIMFILE = "''" - set SEDPORFILE = "''" - if ("$BLOM_RIVER_NUTRIENTS" == TRUE) then - set RIVINFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx0.125v4_20221013.nc'" - else - set RIVINFILE = "''" - endif - if ("$BLOM_N_DEPOSITION" == TRUE) then - set NDEPFILE = "'$DIN_LOC_ROOT/ocn/blom/bndcon/$NDEPFNAME'" - else - set NDEPFILE = "''" - endif -else - echo "$0 ERROR: Cannot deal with GRID = $OCN_GRID " - exit -1 -endif - - -#------------------------------------------------------------------------------ -# Create resolved namelist -#------------------------------------------------------------------------------ -foreach mem ("`seq $NINST_OCN`") - if ( ${NINST_OCN} == '1' ) then - set inststr = '' - else - set inststr = ("`printf '_%4.4d' $mem`") - endif - - # modify namelist variables as specified in user_nl_blom - foreach line ("`grep = $CASEROOT/user_nl_blom$inststr`") - if (`echo "$line" | tr -d ' ' | cut -c -1` != '#') then - set var = `echo "$line" | sed 's/=.*//' | tr '[a-z]' '[A-Z]'` - set val = `echo "$line" | sed 's/.*=//'` - eval 'set $var = "$val"' - endif - end - -cp ocn_in.readme $RUNDIR/. - -cat >! $RUNDIR/ocn_in$inststr << EOF -&LIMITS - NDAY1 = $NDAY1 - NDAY2 = $NDAY2 - IDATE = $IDATE - IDATE0 = $IDATE0 - RUNID = $RUNID - EXPCNF = $EXPCNF - RUNTYP = $RUNTYP - GRFILE = $GRFILE - ICFILE = $ICFILE - PREF = $PREF - BACLIN = $BACLIN - BATROP = $BATROP - MDV2HI = $MDV2HI - MDV2LO = $MDV2LO - MDV4HI = $MDV4HI - MDV4LO = $MDV4LO - MDC2HI = $MDC2HI - MDC2LO = $MDC2LO - VSC2HI = $VSC2HI - VSC2LO = $VSC2LO - VSC4HI = $VSC4HI - VSC4LO = $VSC4LO - CBAR = $CBAR - CB = $CB - CWBDTS = $CWBDTS - CWBDLS = $CWBDLS - MOMMTH = $MOMMTH - BMCMTH = $BMCMTH - RMPMTH = $RMPMTH - MLRTTP = $MLRTTP - RM0 = $RM0 - RM5 = $RM5 - CE = $CE - TDFILE = $TDFILE - NIWGF = $NIWGF - NIWBF = $NIWBF - NIWLF = $NIWLF - SWAMTH = $SWAMTH - JWTYPE = $JWTYPE - CHLOPT = $CHLOPT - CCFILE = $CCFILE - TRXDAY = $TRXDAY - SRXDAY = $SRXDAY - TRXDPT = $TRXDPT - SRXDPT = $SRXDPT - TRXLIM = $TRXLIM - SRXLIM = $SRXLIM - APTFLX = $APTFLX - APSFLX = $APSFLX - DITFLX = $DITFLX - DISFLX = $DISFLX - SRXBAL = $SRXBAL - SCFILE = $SCFILE - WAVSRC = $WAVSRC - SMTFRC = $SMTFRC - SPRFAC = $SPRFAC - ATM_PATH = $ATM_PATH - ITEST = $ITEST - JTEST = $JTEST - CNSVDI = $CNSVDI - CSDIAG = $CSDIAG - RSTFRQ = $RSTFRQ - RSTFMT = $RSTFMT - RSTCMP = $RSTCMP - IOTYPE = $IOTYPE -/ -EOF - -if ("$BLOM_VCOORD" == cntiso_hybrid) then -cat >>! $RUNDIR/ocn_in$inststr << EOF - -&VCOORD - VCOORD_TYPE = $VCOORD_TYPE - RECONSTRUCTION_METHOD = $RECONSTRUCTION_METHOD - DENSITY_LIMITING = $DENSITY_LIMITING - TRACER_LIMITING = $TRACER_LIMITING - VELOCITY_LIMITING = $VELOCITY_LIMITING - DENSITY_PC_UPPER_BNDR = $DENSITY_PC_UPPER_BNDR - DENSITY_PC_LOWER_BNDR = $DENSITY_PC_LOWER_BNDR - TRACER_PC_UPPER_BNDR = $TRACER_PC_UPPER_BNDR - TRACER_PC_LOWER_BNDR = $TRACER_PC_LOWER_BNDR - VELOCITY_PC_UPPER_BNDR = $VELOCITY_PC_UPPER_BNDR - VELOCITY_PC_LOWER_BNDR = $VELOCITY_PC_LOWER_BNDR - DPMIN_SURFACE = $DPMIN_SURFACE - DPMIN_INFLATION_FACTOR = $DPMIN_INFLATION_FACTOR - DPMIN_INTERIOR = $DPMIN_INTERIOR - DKTZU = $DKTZU - DKTZL = $DKTZL -/ -EOF -endif - -cat >>! $RUNDIR/ocn_in$inststr << EOF - -&DIFFUSION - EITMTH = $EITMTH - EDRITP = $EDRITP - EDWMTH = $EDWMTH - EDDF2D = $EDDF2D - EDSPRS = $EDSPRS - EGC = $EGC - EGGAM = $EGGAM - EGLSMN = $EGLSMN - EGMNDF = $EGMNDF - EGMXDF = $EGMXDF - EGIDFQ = $EGIDFQ - TBFILE = $TBFILE - RHISCF = $RHISCF - EDANIS = $EDANIS - REDI3D = $REDI3D - RHSCTP = $RHSCTP - RI0 = $RI0 - BDMTYP = $BDMTYP - BDMC1 = $BDMC1 - BDMC2 = $BDMC2 - BDMLDP = $BDMLDP - TKEPF = $TKEPF - SMOBLD = $SMOBLD - LNGMTP = $LNGMTP - LTEDTP = $LTEDTP -/ -EOF - -if ($?CWMTAG) then -cat >>! $RUNDIR/ocn_in$inststr << EOF - -&CWMOD - CWMTAG = $CWMTAG - CWMEDG = $CWMEDG - CWMI = $CWMI - CWMJ = $CWMJ - CWMWTH = $CWMWTH -/ -EOF -endif - -cat >>! $RUNDIR/ocn_in$inststr << EOF - -&MERDIA - MER_ORFILE = $MER_ORFILE - MER_MIFILE = $MER_MIFILE - MER_REGNAM = 'atlantic_arctic_ocean', 'atlantic_arctic_extended_ocean', 'indian_pacific_ocean', 'global_ocean' - MER_REGFLG(1,:) = 2, 4 - MER_REGFLG(2,:) = 2, 4, 6, 7, 8, 9 - MER_REGFLG(3,:) = 3, 5 - MER_REGFLG(4,:) = 0 - MER_MINLAT = -34., -34., -34., -90. - MER_MAXLAT = 90., 90., 90., 90. -/ - -&SECDIA - SEC_SIFILE = $SEC_SIFILE -/ - -&DIAPHY - GLB_FNAMETAG = $GLB_FNAMETAG - GLB_AVEPERIO = $GLB_AVEPERIO - GLB_FILEFREQ = $GLB_FILEFREQ - GLB_COMPFLAG = $GLB_COMPFLAG - GLB_NCFORMAT = $GLB_NCFORMAT - H2D_ABSWND = $H2D_ABSWND - H2D_ALB = $H2D_ALB - H2D_BTMSTR = $H2D_BTMSTR - H2D_BRNFLX = $H2D_BRNFLX - H2D_BRNPD = $H2D_BRNPD - H2D_DFL = $H2D_DFL - H2D_EVA = $H2D_EVA - H2D_FICE = $H2D_FICE - H2D_FMLTFZ = $H2D_FMLTFZ - H2D_HICE = $H2D_HICE - H2D_HMLTFZ = $H2D_HMLTFZ - H2D_HSNW = $H2D_HSNW - H2D_IAGE = $H2D_IAGE - H2D_IDKEDT = $H2D_IDKEDT - H2D_LAMULT = $H2D_LAMULT - H2D_LASL = $H2D_LASL - H2D_LIP = $H2D_LIP - H2D_MAXMLD = $H2D_MAXMLD - H2D_MLD = $H2D_MLD - H2D_MLTS = $H2D_MLTS - H2D_MLTSMN = $H2D_MLTSMN - H2D_MLTSMX = $H2D_MLTSMX - H2D_MLTSSQ = $H2D_MLTSSQ - H2D_MTKEUS = $H2D_MTKEUS - H2D_MTKENI = $H2D_MTKENI - H2D_MTKEBF = $H2D_MTKEBF - H2D_MTKERS = $H2D_MTKERS - H2D_MTKEPE = $H2D_MTKEPE - H2D_MTKEKE = $H2D_MTKEKE - H2D_MTY = $H2D_MTY - H2D_NSF = $H2D_NSF - H2D_PBOT = $H2D_PBOT - H2D_PSRF = $H2D_PSRF - H2D_RFIFLX = $H2D_RFIFLX - H2D_RNFFLX = $H2D_RNFFLX - H2D_SALFLX = $H2D_SALFLX - H2D_SALRLX = $H2D_SALRLX - H2D_SBOT = $H2D_SBOT - H2D_SEALV = $H2D_SEALV - H2D_SLVSQ = $H2D_SLVSQ - H2D_SFL = $H2D_SFL - H2D_SOP = $H2D_SOP - H2D_SIGMX = $H2D_SIGMX - H2D_SSS = $H2D_SSS - H2D_SSSSQ = $H2D_SSSSQ - H2D_SST = $H2D_SST - H2D_SSTSQ = $H2D_SSTSQ - H2D_SURFLX = $H2D_SURFLX - H2D_SURRLX = $H2D_SURRLX - H2D_SWA = $H2D_SWA - H2D_T20D = $H2D_T20D - H2D_TAUX = $H2D_TAUX - H2D_TAUY = $H2D_TAUY - H2D_TBOT = $H2D_TBOT - H2D_TICE = $H2D_TICE - H2D_TSRF = $H2D_TSRF - H2D_UB = $H2D_UB - H2D_UICE = $H2D_UICE - H2D_USTAR = $H2D_USTAR - H2D_USTAR3 = $H2D_USTAR3 - H2D_USTOKES = $H2D_USTOKES - H2D_VB = $H2D_VB - H2D_VICE = $H2D_VICE - H2D_VSTOKES = $H2D_VSTOKES - H2D_ZTX = $H2D_ZTX - LYR_BFSQ = $LYR_BFSQ - LYR_DIFDIA = $LYR_DIFDIA - LYR_DIFVMO = $LYR_DIFVMO - LYR_DIFVHO = $LYR_DIFVHO - LYR_DIFVSO = $LYR_DIFVSO - LYR_DIFINT = $LYR_DIFINT - LYR_DIFISO = $LYR_DIFISO - LYR_DP = $LYR_DP - LYR_DZ = $LYR_DZ - LYR_SALN = $LYR_SALN - LYR_TEMP = $LYR_TEMP - LYR_TRC = $LYR_TRC - LYR_UFLX = $LYR_UFLX - LYR_UTFLX = $LYR_UTFLX - LYR_USFLX = $LYR_USFLX - LYR_UMFLTD = $LYR_UMFLTD - LYR_UMFLSM = $LYR_UMFLSM - LYR_UTFLTD = $LYR_UTFLTD - LYR_UTFLSM = $LYR_UTFLSM - LYR_UTFLLD = $LYR_UTFLLD - LYR_USFLTD = $LYR_USFLTD - LYR_USFLSM = $LYR_USFLSM - LYR_USFLLD = $LYR_USFLLD - LYR_UVEL = $LYR_UVEL - LYR_VFLX = $LYR_VFLX - LYR_VTFLX = $LYR_VTFLX - LYR_VSFLX = $LYR_VSFLX - LYR_VMFLTD = $LYR_VMFLTD - LYR_VMFLSM = $LYR_VMFLSM - LYR_VTFLTD = $LYR_VTFLTD - LYR_VTFLSM = $LYR_VTFLSM - LYR_VTFLLD = $LYR_VTFLLD - LYR_VSFLTD = $LYR_VSFLTD - LYR_VSFLSM = $LYR_VSFLSM - LYR_VSFLLD = $LYR_VSFLLD - LYR_VVEL = $LYR_VVEL - LYR_WFLX = $LYR_WFLX - LYR_WFLX2 = $LYR_WFLX2 - LYR_PV = $LYR_PV - LYR_TKE = $LYR_TKE - LYR_GLS_PSI = $LYR_GLS_PSI - LYR_IDLAGE = $LYR_IDLAGE - LVL_BFSQ = $LVL_BFSQ - LVL_DIFDIA = $LVL_DIFDIA - LVL_DIFVMO = $LVL_DIFVMO - LVL_DIFVHO = $LVL_DIFVHO - LVL_DIFVSO = $LVL_DIFVSO - LVL_DIFINT = $LVL_DIFINT - LVL_DIFISO = $LVL_DIFISO - LVL_DZ = $LVL_DZ - LVL_SALN = $LVL_SALN - LVL_TEMP = $LVL_TEMP - LVL_TRC = $LVL_TRC - LVL_UFLX = $LVL_UFLX - LVL_UTFLX = $LVL_UTFLX - LVL_USFLX = $LVL_USFLX - LVL_UMFLTD = $LVL_UMFLTD - LVL_UMFLSM = $LVL_UMFLSM - LVL_UTFLTD = $LVL_UTFLTD - LVL_UTFLSM = $LVL_UTFLSM - LVL_UTFLLD = $LVL_UTFLLD - LVL_USFLTD = $LVL_USFLTD - LVL_USFLSM = $LVL_USFLSM - LVL_USFLLD = $LVL_USFLLD - LVL_UVEL = $LVL_UVEL - LVL_VFLX = $LVL_VFLX - LVL_VTFLX = $LVL_VTFLX - LVL_VSFLX = $LVL_VSFLX - LVL_VMFLTD = $LVL_VMFLTD - LVL_VMFLSM = $LVL_VMFLSM - LVL_VTFLTD = $LVL_VTFLTD - LVL_VTFLSM = $LVL_VTFLSM - LVL_VTFLLD = $LVL_VTFLLD - LVL_VSFLTD = $LVL_VSFLTD - LVL_VSFLSM = $LVL_VSFLSM - LVL_VSFLLD = $LVL_VSFLLD - LVL_VVEL = $LVL_VVEL - LVL_WFLX = $LVL_WFLX - LVL_WFLX2 = $LVL_WFLX2 - LVL_PV = $LVL_PV - LVL_TKE = $LVL_TKE - LVL_GLS_PSI = $LVL_GLS_PSI - LVL_IDLAGE = $LVL_IDLAGE - MSC_MMFLXL = $MSC_MMFLXL - MSC_MMFLXD = $MSC_MMFLXD - MSC_MMFTDL = $MSC_MMFTDL - MSC_MMFSML = $MSC_MMFSML - MSC_MMFTDD = $MSC_MMFTDD - MSC_MMFSMD = $MSC_MMFSMD - MSC_MHFLX = $MSC_MHFLX - MSC_MHFTD = $MSC_MHFTD - MSC_MHFSM = $MSC_MHFSM - MSC_MHFLD = $MSC_MHFLD - MSC_MSFLX = $MSC_MSFLX - MSC_MSFTD = $MSC_MSFTD - MSC_MSFSM = $MSC_MSFSM - MSC_MSFLD = $MSC_MSFLD - MSC_VOLTR = $MSC_VOLTR - MSC_MASSGS = $MSC_MASSGS - MSC_VOLGS = $MSC_VOLGS - MSC_SALNGA = $MSC_SALNGA - MSC_TEMPGA = $MSC_TEMPGA - MSC_SSSGA = $MSC_SSSGA - MSC_SSTGA = $MSC_SSTGA -/ -EOF - -if ("$ecosys" == TRUE) then -cat >>! $RUNDIR/ocn_in$inststr << EOF - -&BGCNML - ATM_CO2 = $CCSM_CO2_PPMV - FEDEPFILE = $FEDEPFILE - SWACLIMFILE = $SWACLIMFILE - DO_RIVINPT = $DO_RIVINPT - RIVINFILE = $RIVINFILE - DO_NDEP = $DO_NDEP - NDEPFILE = $NDEPFILE - DO_OALK = $DO_OALK - DO_SEDSPINUP = $DO_SEDSPINUP - SEDSPIN_YR_S = $SEDSPIN_YR_S - SEDSPIN_YR_E = $SEDSPIN_YR_E - SEDSPIN_NCYC = $SEDSPIN_NCYC - INIDIC = $INIDIC - INIALK = $INIALK - INIPO4 = $INIPO4 - INIOXY = $INIOXY - ININO3 = $ININO3 - INISIL = $INISIL - INID13C = $INID13C - INID14C = $INID14C - WITH_DMSPH = $WITH_DMSPH - PI_PH_FILE = $PI_PH_FILE - L_3DVARSEDPOR = $L_3DVARSEDPOR - SEDPORFILE = $SEDPORFILE -/ - -&BGCPARAMS -/ - -&BGCOAFX - OALKSCEN = $BGCOAFX_OALKSCEN - OALKFILE = $BGCOAFX_OALKFILE - ADDALK = $BGCOAFX_ADDALK - CDRMIP_LATMAX = $BGCOAFX_CDRMIP_LATMAX - CDRMIP_LATMIN = $BGCOAFX_CDRMIP_LATMIN - RAMP_START = $BGCOAFX_RAMP_START - RAMP_END = $BGCOAFX_RAMP_END -/ - -&DIABGC - GLB_FNAMETAG = $BGC_FNAMETAG - GLB_AVEPERIO = $BGC_AVEPERIO - GLB_FILEFREQ = $BGC_FILEFREQ - GLB_COMPFLAG = $BGC_COMPFLAG - GLB_NCFORMAT = $BGC_NCFORMAT - GLB_INVENTORY = $BGC_INVENTORY - SRF_PHOSPH = $SRF_PHOSPH - SRF_OXYGEN = $SRF_OXYGEN - SRF_IRON = $SRF_IRON - SRF_ANO3 = $SRF_ANO3 - SRF_ALKALI = $SRF_ALKALI - SRF_SILICA = $SRF_SILICA - SRF_DIC = $SRF_DIC - SRF_PHYTO = $SRF_PHYTO - SRF_PH = $SRF_PH - SRF_EXPORT = $SRF_EXPORT - SRF_EXPOSI = $SRF_EXPOSI - SRF_EXPOCA = $SRF_EXPOCA - SRF_KWCO2 = $SRF_KWCO2 - SRF_KWCO2KHM = $SRF_KWCO2KHM - SRF_CO2KH = $SRF_CO2KH - SRF_CO2KHM = $SRF_CO2KHM - SRF_PCO2 = $SRF_PCO2 - SRF_PCO2M = $SRF_PCO2M - SRF_CO2FXD = $SRF_CO2FXD - SRF_CO2FXU = $SRF_CO2FXU - SRF_OXFLUX = $SRF_OXFLUX - SRF_NIFLUX = $SRF_NIFLUX - SRF_N2OFX = $SRF_N2OFX - SRF_DMSFLUX = $SRF_DMSFLUX - SRF_DMS = $SRF_DMS - SRF_DMSPROD = $SRF_DMSPROD - SRF_DMS_BAC = $SRF_DMS_BAC - SRF_DMS_UV = $SRF_DMS_UV - SRF_ATMCO2 = $SRF_ATMCO2 - SRF_ATMO2 = $SRF_ATMO2 - SRF_ATMN2 = $SRF_ATMN2 - SRF_NATDIC = $SRF_NATDIC - SRF_NATALKALI = $SRF_NATALKALI - SRF_NATPH = $SRF_NATPH - SRF_NATPCO2 = $SRF_NATPCO2 - SRF_NATCO2FX = $SRF_NATCO2FX - SRF_CO213FXD = $SRF_CO213FXD - SRF_CO213FXU = $SRF_CO213FXU - SRF_CO214FXD = $SRF_CO214FXD - SRF_CO214FXU = $SRF_CO214FXU - SRF_CFC11 = $SRF_CFC11 - SRF_CFC12 = $SRF_CFC12 - SRF_SF6 = $SRF_SF6 - SRF_BROMO = $SRF_BROMO - SRF_BROMOFX = $SRF_BROMOFX - INT_BROMOPRO = $INT_BROMOPRO - INT_BROMOUV = $INT_BROMOUV - INT_PHOSY = $INT_PHOSY - INT_NFIX = $INT_NFIX - INT_DNIT = $INT_DNIT - FLX_NDEP = $FLX_NDEP - FLX_OALK = $FLX_OALK - FLX_CAR0100 = $FLX_CAR0100 - FLX_CAR0500 = $FLX_CAR0500 - FLX_CAR1000 = $FLX_CAR1000 - FLX_CAR2000 = $FLX_CAR2000 - FLX_CAR4000 = $FLX_CAR4000 - FLX_CAR_BOT = $FLX_CAR_BOT - FLX_BSI0100 = $FLX_BSI0100 - FLX_BSI0500 = $FLX_BSI0500 - FLX_BSI1000 = $FLX_BSI1000 - FLX_BSI2000 = $FLX_BSI2000 - FLX_BSI4000 = $FLX_BSI4000 - FLX_BSI_BOT = $FLX_BSI_BOT - FLX_CAL0100 = $FLX_CAL0100 - FLX_CAL0500 = $FLX_CAL0500 - FLX_CAL1000 = $FLX_CAL1000 - FLX_CAL2000 = $FLX_CAL2000 - FLX_CAL4000 = $FLX_CAL4000 - FLX_CAL_BOT = $FLX_CAL_BOT - LYR_PHYTO = $LYR_PHYTO - LYR_GRAZER = $LYR_GRAZER - LYR_DOC = $LYR_DOC - LYR_PHOSY = $LYR_PHOSY - LYR_PHOSPH = $LYR_PHOSPH - LYR_OXYGEN = $LYR_OXYGEN - LYR_IRON = $LYR_IRON - LYR_ANO3 = $LYR_ANO3 - LYR_ALKALI = $LYR_ALKALI - LYR_SILICA = $LYR_SILICA - LYR_DIC = $LYR_DIC - LYR_POC = $LYR_POC - LYR_CALC = $LYR_CALC - LYR_OPAL = $LYR_OPAL - LYR_CO3 = $LYR_CO3 - LYR_N2O = $LYR_N2O - LYR_PH = $LYR_PH - LYR_OMEGAC = $LYR_OMEGAC - LYR_OMEGAA = $LYR_OMEGAA - LYR_PREFO2 = $LYR_PREFO2 - LYR_O2SAT = $LYR_O2SAT - LYR_PREFPO4 = $LYR_PREFPO4 - LYR_PREFALK = $LYR_PREFALK - LYR_PREFDIC = $LYR_PREFDIC - LYR_DICSAT = $LYR_DICSAT - LYR_NATDIC = $LYR_NATDIC - LYR_NATALKALI = $LYR_NATALKALI - LYR_NATCO3 = $LYR_NATCO3 - LYR_NATCALC = $LYR_NATCALC - LYR_NATPH = $LYR_NATPH - LYR_NATOMEGAC = $LYR_NATOMEGAC - LYR_NATOMEGAA = $LYR_NATOMEGAA - LYR_DIC13 = $LYR_DIC13 - LYR_DIC14 = $LYR_DIC14 - LYR_D13C = $LYR_D13C - LYR_D14C = $LYR_D14C - LYR_BIGD14C = $LYR_BIGD14C - LYR_POC13 = $LYR_POC13 - LYR_DOC13 = $LYR_DOC13 - LYR_CALC13 = $LYR_CALC13 - LYR_PHYTO13 = $LYR_PHYTO13 - LYR_GRAZER13 = $LYR_GRAZER13 - LYR_CFC11 = $LYR_CFC11 - LYR_CFC12 = $LYR_CFC12 - LYR_SF6 = $LYR_SF6 - LYR_NOS = $LYR_NOS - LYR_WPHY = $LYR_WPHY - LYR_WNOS = $LYR_WNOS - LYR_EPS = $LYR_EPS - LYR_ASIZE = $LYR_ASIZE - LYR_DP = $BGC_DP - LYR_BROMO = $LYR_BROMO - LVL_PHYTO = $LVL_PHYTO - LVL_GRAZER = $LVL_GRAZER - LVL_DOC = $LVL_DOC - LVL_PHOSY = $LVL_PHOSY - LVL_PHOSPH = $LVL_PHOSPH - LVL_OXYGEN = $LVL_OXYGEN - LVL_IRON = $LVL_IRON - LVL_ANO3 = $LVL_ANO3 - LVL_ALKALI = $LVL_ALKALI - LVL_SILICA = $LVL_SILICA - LVL_DIC = $LVL_DIC - LVL_POC = $LVL_POC - LVL_CALC = $LVL_CALC - LVL_OPAL = $LVL_OPAL - LVL_CO3 = $LVL_CO3 - LVL_N2O = $LVL_N2O - LVL_PH = $LVL_PH - LVL_OMEGAC = $LVL_OMEGAC - LVL_OMEGAA = $LVL_OMEGAA - LVL_PREFO2 = $LVL_PREFO2 - LVL_O2SAT = $LVL_O2SAT - LVL_PREFPO4 = $LVL_PREFPO4 - LVL_PREFALK = $LVL_PREFALK - LVL_PREFDIC = $LVL_PREFDIC - LVL_DICSAT = $LVL_DICSAT - LVL_NATDIC = $LVL_NATDIC - LVL_NATALKALI = $LVL_NATALKALI - LVL_NATCO3 = $LVL_NATCO3 - LVL_NATCALC = $LVL_NATCALC - LVL_NATPH = $LVL_NATPH - LVL_NATOMEGAC = $LVL_NATOMEGAC - LVL_NATOMEGAA = $LVL_NATOMEGAA - LVL_DIC13 = $LVL_DIC13 - LVL_DIC14 = $LVL_DIC14 - LVL_D13C = $LVL_D13C - LVL_POC13 = $LVL_POC13 - LVL_DOC13 = $LVL_DOC13 - LVL_CALC13 = $LVL_CALC13 - LVL_PHYTO13 = $LVL_PHYTO13 - LVL_GRAZER13 = $LVL_GRAZER13 - LVL_CFC11 = $LVL_CFC11 - LVL_CFC12 = $LVL_CFC12 - LVL_SF6 = $LVL_SF6 - LVL_NOS = $LVL_NOS - LVL_WPHY = $LVL_WPHY - LVL_WNOS = $LVL_WNOS - LVL_EPS = $LVL_EPS - LVL_ASIZE = $LVL_ASIZE - LVL_BROMO = $LVL_BROMO - FLX_SEDIFFIC = $FLX_SEDIFFIC - FLX_SEDIFFAL = $FLX_SEDIFFAL - FLX_SEDIFFPH = $FLX_SEDIFFPH - FLX_SEDIFFOX = $FLX_SEDIFFOX - FLX_SEDIFFN2 = $FLX_SEDIFFN2 - FLX_SEDIFFNO3 = $FLX_SEDIFFNO3 - FLX_SEDIFFSI = $FLX_SEDIFFSI - SDM_POWAIC = $SDM_POWAIC - SDM_POWAAL = $SDM_POWAAL - SDM_POWAPH = $SDM_POWAPH - SDM_POWAOX = $SDM_POWAOX - SDM_POWN2 = $SDM_POWN2 - SDM_POWNO3 = $SDM_POWNO3 - SDM_POWASI = $SDM_POWASI - SDM_SSSO12 = $SDM_SSSO12 - SDM_SSSSIL = $SDM_SSSSIL - SDM_SSSC12 = $SDM_SSSC12 - SDM_SSSTER = $SDM_SSSTER - BUR_SSSO12 = $BUR_SSSO12 - BUR_SSSSIL = $BUR_SSSSIL - BUR_SSSC12 = $BUR_SSSC12 - BUR_SSSTER = $BUR_SSSTER -/ -EOF -endif - -end ## foreach mem ("`seq $NINST_OCN`") - -#------------------------------------------------------------------------------ -# Generate blom.input_data_list -#------------------------------------------------------------------------------ - -cat > $CASEBUILD/blom.input_data_list << EOF -grid_file = `echo $GRFILE | tr -d '"' | tr -d "'"` -meridional_transport_index_file = `echo $MER_MIFILE | tr -d '"' | tr -d "'"` -meridional_transport_basin_file = `echo $MER_ORFILE | tr -d '"' | tr -d "'"` -section_index_file = `echo $SEC_SIFILE | tr -d '"' | tr -d "'"` -tidal_dissipation_file = `echo $TDFILE | tr -d '"' | tr -d "'"` -EOF -if ($SWAMTH == "'chlorophyll'") then -cat >> $CASEBUILD/blom.input_data_list << EOF -chlorophyll_concentration_file = `echo $CCFILE | tr -d '"' | tr -d "'"` -EOF -endif -if ($SRXDAY != "0.") then -cat >> $CASEBUILD/blom.input_data_list << EOF -sss_climatology_file = `echo $SCFILE | tr -d '"' | tr -d "'"` -EOF -endif - -# iHAMOCC boundary conditions -if ($ecosys == TRUE) then -cat >> $CASEBUILD/blom.input_data_list << EOF -dust_file = `echo $FEDEPFILE | tr -d '"' | tr -d "'"` -EOF - if ($BLOM_RIVER_NUTRIENTS == TRUE) then -cat >> $CASEBUILD/blom.input_data_list << EOF -river_file = `echo $RIVINFILE | tr -d '"' | tr -d "'"` -EOF - endif - if ($BLOM_N_DEPOSITION == TRUE) then -cat >> $CASEBUILD/blom.input_data_list << EOF -n_deposition_file = `echo $NDEPFILE | tr -d '"' | tr -d "'"` -EOF - endif - if ($BGCOAFX_OALKFILE != "''") then -cat >> $CASEBUILD/blom.input_data_list << EOF -oafx_file = `echo $BGCOAFX_OALKFILE | tr -d '"' | tr -d "'"` -EOF - endif - if ($HAMOCC_VSLS == TRUE) then -cat >> $CASEBUILD/blom.input_data_list << EOF -swa_clim_file = `echo $SWACLIMFILE | tr -d '"' | tr -d "'"` -EOF - endif - if ($L_3DVARSEDPOR == TRUE) then -cat >> $CASEBUILD/blom.input_data_list << EOF -sed_porosity_file = `echo $SEDPORFILE | tr -d '"' | tr -d "'"` -EOF - endif -endif - -# BLOM initial conditions -cat >> $CASEBUILD/blom.input_data_list << EOF -inicon_file = `echo $ICFILE | tr -d '"' | tr -d "'"` -EOF - - -# iHAMOCC initial conditions -if ($ecosys == TRUE) then -cat >> $CASEBUILD/blom.input_data_list << EOF -inidic_file = `echo $INIDIC | tr -d '"' | tr -d "'"` -inialk_file = `echo $INIALK | tr -d '"' | tr -d "'"` -inipo4_file = `echo $INIPO4 | tr -d '"' | tr -d "'"` -inioxy_file = `echo $INIOXY | tr -d '"' | tr -d "'"` -inino3_file = `echo $ININO3 | tr -d '"' | tr -d "'"` -inisil_file = `echo $INISIL | tr -d '"' | tr -d "'"` -EOF - if ($HAMOCC_CISO == TRUE) then -cat >> $CASEBUILD/blom.input_data_list << EOF -inic13_file = `echo $INID13C | tr -d '"' | tr -d "'"` -inic14_file = `echo $INID14C | tr -d '"' | tr -d "'"` -EOF - endif - if ($RUN_TYPE == startup) then - if ($ICFILE =~ *.blom.r.*) then -cat >> $CASEBUILD/blom.input_data_list << EOF -inicon_bgc_file = `echo $ICFILE | sed 's/.blom.r./.blom.rbgc./' | tr -d '"' | tr -d "'"` -EOF - else if ($ICFILE =~ *.micom.r.*) then -cat >> $CASEBUILD/blom.input_data_list << EOF -inicon_bgc_file = `echo $ICFILE | sed 's/.micom.r./.micom.rbgc./' | tr -d '"' | tr -d "'"` -EOF - endif - endif -endif - - +#!/usr/bin/env python3 + +""" +BLOM namelist creator +""" +import sys +import os +import shutil +import logging +import glob + +_CIMEROOT = os.environ.get("CIMEROOT") +if _CIMEROOT is None: + raise SystemExit("ERROR: must set CIMEROOT environment variable") + +_LIBDIR = os.path.join(_CIMEROOT, "CIME", "Tools") +if os.path.exists(_LIBDIR): + sys.path.append(_LIBDIR) +else: + _LIBDIR = os.path.join(_CIMEROOT,"scripts","lib","CIME") + sys.path.append(os.path.join(_CIMEROOT, "scripts", "Tools")) + +if not os.path.exists(_LIBDIR): + raise SystemExit("ERROR: _LIBDIR does not exist") + +# Save local (cime_config) directory path: +_CIME_CONFIG_PATH = os.path.dirname(os.path.abspath(__file__)) + +# Add local (cime_config) directory to python path: +sys.path.append(_CIME_CONFIG_PATH) + +# Add ability to handle using a case as a dictionary rather a CIME case object +# pylint: disable=wildcard-import, wrong-import-position +# pylint: disable=unused-wildcard-import +from standard_script_setup import * +from CIME.buildnml import create_namelist_infile, parse_input +from CIME.case import Case +from CIME.utils import expect +from case_dict import CaseDict + +# Import BLOM's ParamGen class: +from ocn_in_paramgen import OcnInParamGen + +# Open CIME case log: +_LOGGER = logging.getLogger(__name__) + +############################################################################### +def buildnml(case, caseroot, compname): +############################################################################### + # pylint: disable=too-many-locals + """Build the blom namelist """ + + # Build the component namelist + if compname != "blom": + emsg = "BLOM buildnml called with model={}" + raise AttributeError(emsg.format(compname)) + # End if + + srcroot = case.get_value("SRCROOT") + din_loc_root = case.get_value("DIN_LOC_ROOT") + caseroot = case.get_value("CASEROOT") + rundir = case.get_value("RUNDIR") + continue_run = case.get_value("CONTINUE_RUN") + ninst_ocn = case.get_value("NINST_OCN") + run_type = case.get_value("RUN_TYPE") + run_startdate = case.get_value("RUN_STARTDATE") + run_refcase = case.get_value("RUN_REFCASE") + run_refdate = case.get_value("RUN_REFDATE") + run_reftod = case.get_value("RUN_REFTOD") + ocn_grid = case.get_value("OCN_GRID") + ocn_ncpl = case.get_value("OCN_NCPL") + pio_typename_ocn = case.get_value("PIO_TYPENAME_OCN") + pio_netcdf_format_ocn = case.get_value("PIO_NETCDF_FORMAT_OCN") + blom_unit = case.get_value("BLOM_UNIT") + blom_vcoord = case.get_value("BLOM_VCOORD") + blom_river_nutrients = case.get_value("BLOM_RIVER_NUTRIENTS") + blom_n_deposition = case.get_value("BLOM_N_DEPOSITION") + blom_ndep_scenario = case.get_value("BLOM_NDEP_SCENARIO") + blom_coupling = case.get_value("BLOM_COUPLING") + blom_tracer_modules = case.get_value("BLOM_TRACER_MODULES") + hamocc_ciso = case.get_value("HAMOCC_CISO") + hamocc_sedspinup = case.get_value("HAMOCC_SEDSPINUP") + hamocc_sedspinup_yr_start = case.get_value("HAMOCC_SEDSPINUP_YR_START") + hamocc_sedspinup_yr_end = case.get_value("HAMOCC_SEDSPINUP_YR_END") + hamocc_sedspinup_ncycle = case.get_value("HAMOCC_SEDSPINUP_NCYCLE") + is_test = case.get_value("TEST") + + #-------------------------- + # Construct ParamGen objects: + #-------------------------- + + xml_fil = os.path.join(srcroot,"components","blom","cime_config","namelist_definition_blom.xml") + pg_blom = OcnInParamGen.from_namelist_xml(xml_fil) + + #------------------------ + # Loop over all instances: + #------------------------ + + # Set input data list file name: + input_data_list = os.path.join(caseroot, "Buildconf", "blom.input_data_list") + + # Convert instance number to integer: + ninst = int(ninst_ocn) + + for inst_counter in range(1, ninst+1): + + #---------------------- + # Remove old input data: + #---------------------- + + if os.path.isfile(input_data_list): + os.remove(input_data_list) + + # ----------------------------------------------------- + # Determine instance string + # ----------------------------------------------------- + + inst_string = "" + if ninst > 1: + single_case_rpointer = os.path.join(rundir, "rpointer.ocn") + inst_string = '_%04d' % inst_counter + instance_rpointer = os.path.join(rundir, "rpointer.ocn"+inst_string) + + # If multi-instance case does not have restart file, use + # single-case restart for each instance + + if os.path.isfile(single_case_rpointer) and \ + not os.path.isfile(instance_rpointer): + shutil.copy(single_case_rpointer, instance_rpointer) + # End if + # End if + + # ----------------------------------------------------- + # Create blomconf/namelist + # ----------------------------------------------------- + + infile_lines = [] + + # Determine location and name of "user_nl_blom" files: + user_nl_file = os.path.join(caseroot, "user_nl_blom" + inst_string) + + # Determine location and name of namelist input file: + confdir = os.path.join(caseroot,"Buildconf","blomconf") + if not os.path.isdir(confdir): + os.makedirs(confdir) + namelist_infile = os.path.join(confdir, "namelist_infile") + + #-------------------------------- + # Create CIME namelist input file: + #-------------------------------- + create_namelist_infile(case, user_nl_file, namelist_infile, + "\n".join(infile_lines)) + + #------------------------------------------- + # Add user_nl_blom entries to ParamGen object: + #------------------------------------------- + + pg_blom.append_user_nl_file(user_nl_file) + + #------------------------------------------------- + # Create config dictionary + #------------------------------------------------- + + # create config dictionary - used in namelist_definition_blom.xml + config = {} + config['ocn_grid'] = ocn_grid + config['ocn_ncpl'] = str(ocn_ncpl) + config['pio_typename_ocn'] = pio_typename_ocn + config['pio_netcdf_format_ocn'] = pio_netcdf_format_ocn + config["continue_run"] = "yes" if continue_run else "no" + config['blom_unit'] = blom_unit if blom_unit else "unset" + config['blom_vcoord'] = blom_vcoord + config["blom_river_nutrients"] = "yes" if blom_river_nutrients else "no" + config["blom_ndep_scenario"] = "ssp" if "ssp" in blom_ndep_scenario else blom_ndep_scenario + config["blom_n_deposition"] = "yes" if blom_n_deposition else "no" + config["blom_coupling"] = blom_coupling + config["blom_tracer_modules"] = blom_tracer_modules + config["hamocc_ciso"] = "yes" if hamocc_ciso else "no" + config["hamocc_sedspinup"] = "yes" if hamocc_sedspinup else "no" + config["hamocc_sedspinup_yr_start"] = hamocc_sedspinup_yr_start + config["hamocc_sedspinup_yr_end"] = hamocc_sedspinup_yr_end + config["hamocc_sedspinup_ncycle"] = hamocc_sedspinup_ncycle + config["is_test"] = "yes" if is_test else "no" + + # TODO: the following needs to have new ways to turn this to "yes" + config["use_agg"] = "no" + config["use_wlin"] = "yes" + + if is_test: + testcase = case.get_value("TESTCASE") + config["is_test_pfs"] = "yes" if testcase == "PFS" else "no" + config["empty_hist"] = "yes" if testcase == "PFS" else "no" + + #--------------------------------- + # Set all ParamGen namelist values: + #--------------------------------- + + case_dict = CaseDict() + var_list = ('DIN_LOC_ROOT', 'RUN_TYPE', 'BLOM_VCOORD', + 'CCSM_CO2_PPMV', 'HAMOCC_SEDSPINUP_YR_START', + 'HAMOCC_SEDSPINUP_YR_END') + + for item in var_list: + case_dict[item] = case.get_value(item) + + pg_blom.reduce_ocn_in(case_dict, config) + + #---------------------------------------------------- + # Reset values of some variables + #---------------------------------------------------- + + run_startdate = case.get_value("RUN_STARTDATE") + idate = run_startdate.replace('-','') + pg_blom.set_value("idate", idate) + pg_blom.set_value("idate0", idate) + + if pg_blom.get_value('swaclimfile') == 'UNSET': + pg_blom.set_value('swaclimfile', value='') + + if pg_blom.get_value('inid13c') == 'UNSET': + pg_blom.set_value('inid13c', value='') + + if pg_blom.get_value('inid14c') == 'UNSET': + pg_blom.set_value('inid14c', value='') + + #--------------------------- + # Write out Fortran namelist: + #--------------------------- + + # Create resolved BLOM namelist file name: + namelist_file = os.path.join(rundir, "ocn_in") + + # Change namelist file name depending on instance: + namelist_file += inst_string + + # Create BLOM namelist + groups=['limits','diffusion','merdia','secdia','diaphy'] + + groups.append('cwmod') + + if case.get_value("BLOM_VCOORD") == "cntiso_hybrid": + groups.append('vcoord') + + if "ecosys" in case.get_value("BLOM_TRACER_MODULES"): + groups.append("bgcnml") + groups.append("bgcoafx") + groups.append("diabgc") + + # for now don't write out bgcparams - just create an empty namelist + # if "ecosys" in case.get_value("BLOM_TRACER_MODULES"): + # groups.append("bgcparams") + + pg_blom.write_nmlfile(namelist_file, groups, empty_namelists=["bgcparams"]) + + # Replace MER_REGFLAG1 -> MER_REGFLAG4 with array syntax in ocn_in + from pathlib import Path + filename = Path(namelist_file) + filename.write_text(filename.read_text().replace('mer_regflg1','mer_regflg(1,:)')) + filename.write_text(filename.read_text().replace('mer_regflg2','mer_regflg(2,:)')) + filename.write_text(filename.read_text().replace('mer_regflg3','mer_regflg(3,:)')) + filename.write_text(filename.read_text().replace('mer_regflg4','mer_regflg(4,:)')) + + # Write out blom.input_data_list + data_list_path = os.path.join(case.get_case_root(), "Buildconf", "blom.input_data_list") + if os.path.exists(data_list_path): + os.remove(data_list_path) + pg_blom.write_inputdata(data_list_path, groups) + + # To compare with previous namelist generation, make namelist variable uppercase and + # use single quotes instead of back quotes and make sure that file variables set to UNSET + # get changed to blank quotes + namelist_file_temp = os.path.join(confdir, "ocn_in_temp") + with open(namelist_file, "r") as fread: + with open(namelist_file_temp, "w") as fwrite: + for line_read in fread: + # replace single quote with double quote to generate same namelist as using + # csh buildnml + line = line_read.replace('"',"'") + # make all namelists upper case to generate same namelist as using csh buildnml + tokens = line.split('=',maxsplit=1) + if len(tokens) == 2: + # Change UNSET values for files to lower case + if 'UNSET' in tokens[1]: + tokens[1] = "'unset'" + fwrite.write(f" {tokens[0].upper().strip()} = {tokens[1].strip()}\n") + else: + fwrite.write(line) + shutil.move(namelist_file_temp, namelist_file) + +############################################################################### +def _main_func(): + + caseroot = parse_input(sys.argv) + with Case(caseroot) as case: + buildnml(case, caseroot, "blom") + # End with + +if __name__ == "__main__": + _main_func() diff --git a/cime_config/case_dict.py b/cime_config/case_dict.py new file mode 100644 index 00000000..6a747555 --- /dev/null +++ b/cime_config/case_dict.py @@ -0,0 +1,9 @@ +class CaseDict(dict): + """Class to handle using a case as a dictionary rather + than a CIME case object""" + + def get_value(self, key): + if key in self: + return self[key] + else: + return None diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index a38225a8..d1743b17 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -206,18 +206,6 @@ Set preprocessor option to activate the debugging mode for iHAMOCC. Requires module ecosys - - logical - TRUE,FALSE - FALSE - - TRUE - - build_component_blom - env_build.xml - Set preprocessor option to activate the VSLS-Bromoform tracer code. Requires module ecosys - - char diff --git a/cime_config/config_pes.xml b/cime_config/config_pes.xml index a7f93d71..ac18996f 100644 --- a/cime_config/config_pes.xml +++ b/cime_config/config_pes.xml @@ -493,6 +493,43 @@ + + + + Medium sized pe-layout with pes in total + + 15 + 10 + 76 + 155 + 76 + 1 + 1 + 1 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 76 + 0 + 91 + 0 + 101 + 0 + 0 + 0 + + + + @@ -570,13 +607,13 @@ - Medium sized pe-layout with 352 pes in total + Medium sized pe-layout with 384 pes in total - 1 - 1 + 18 + 18 76 272 - 80 + 76 1 1 1 @@ -592,13 +629,13 @@ 1 - 0 - 1 - 2 - 4 - 80 - 3 - 3 + 76 + 0 + 94 + 0 + 112 + 0 + 0 0 diff --git a/cime_config/namelist_definition_blom.xml b/cime_config/namelist_definition_blom.xml new file mode 100644 index 00000000..119c9e39 --- /dev/null +++ b/cime_config/namelist_definition_blom.xml @@ -0,0 +1,6502 @@ + + + + + + + + + + + + integer + limits + limits + + 0 + + First day of integration (i) + + + + integer + limits + limits + + 0 + + Last day of integration (i) + + + + integer + limits + limits + + 9999999 + + Model date in YYYYMMDD (i) + + + + integer + limits + limits + + 9999999 + + Initial experiment date in YYYYMMDD (i) + + + + limits + limits + + unset + + Experiment name (a) + char + + + + char + limits + limits + + cesm + + Experiment configuration (a) + + + + char + limits + limits + + $RUN_TYPE + continue + + run type (a) + + + + char + limits + limits + + unset + $DIN_LOC_ROOT/ocn/blom/grid/grid_tnx2v1_20130206.nc + $DIN_LOC_ROOT/ocn/blom/grid/grid_tnx1v4_20170622.nc + $DIN_LOC_ROOT/ocn/blom/grid/grid_tnx0.25v4_20170622.nc + $DIN_LOC_ROOT/ocn/blom/grid/grid_tnx0.125v4_20221013.nc + + Name of file containing grid specification (a)Name of file containing grid specification + + + + char + limits + limits + + unset + $DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx2v1_20130419.nc + $DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx1v4_20170622.nc + $DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx0.25v4_20170623.nc + $DIN_LOC_ROOT/ocn/blom/inicon/inicon_tnx0.125v4_20230318.nc + + + Name of file containing initial conditions, that is either a + valid restart file or 'inicon.nc' if climatological based + initial conditions are desired. + + + + + real + limits + limits + + 2000.e4 + 2000.e5 + + Reference pressure for potential density (g/cm/s2) (f) + + + + real + limits + limits + + 1800. + 4800. + 3600. + 4800. + 3200. + 3600. + 900. + 300. + + Baroclinic time step (sec) (f) + + + + real + limits + limits + + 36. + 96. + 60. + 96. + 64. + 60. + 15. + 6. + + Barotropic time step (sec) (f) + + + + real + limits + limits + + .02 + 2. + .15 + .0015 + .1 + .001 + + Laplacian diffusion velocity for momentum dissipation (cm/s) (f) + + + + real + limits + limits + + .004 + .4 + .15 + .0015 + .1 + .001 + + Laplacian diffusion velocity for momentum dissipation (cm/s) (f) + + + + real + limits + limits + + 0. + + Biharmonic diffusion velocity for momentum dissipation (cm/s) (f) + + + + real + limits + limits + + 0. + + Biharmonic diffusion velocity for momentum dissipation (cm/s) (f) + + + + real + limits + limits + + 5000. + 5000.e4 + 300.e4 + 300. + 300.e4 + 300. + + Laplacian diffusivity for momentum dissipation (cm**2/s) (f) + + + + real + limits + limits + + 300. + 300.e4 + 300.e4 + 300. + 100.e4 + 100. + + Laplacian diffusivity for momentum dissipation (cm**2/s) (f) + + + + real + limits + limits + + .5 + .15 + 0. + + Parameter in deformation-dependent Laplacian viscosity (f) + + + + real + limits + limits + + .5 + .15 + 0. + + Parameter in deformation-dependent Laplacian viscosity (f) + + + + real + limits + limits + + 0. + 0.0625 + 0.06 + + Parameter in deformation-dependent Biharmonic viscosity (f) + + + + real + limits + limits + + 0. + 0.0625 + 0.06 + + Parameter in deformation-dependent Biharmonic viscosity (f) + + + + real + limits + limits + + .05 + 5. + + rms flow speed for linear bottom friction law (cm/s) (f) + + + + real + limits + limits + + .002 + + Nondiemnsional coefficient of quadratic bottom friction (f) + + + + real + limits + limits + + 5.e-5 + .75e-4 + 0.75e-4 + .75e-4 + + Coastal wave breaking damping resiprocal time scale (1/s) (f) + + + + real + limits + limits + + 25. + + Coastal wave breaking damping length scale (m) (f) + + + + char + limits + limits + + enscon + + Momentum equation discretization method. Valid methods: + + + + char + limits + limits + + uc + + Baroclinic mass flux correction method. Valid methods: + + + + char + limits + limits + + eitvel + + Method of applying eddy-induced transport in the remap + + + + char + limits + limits + + constant + + Type of mixed layer restratification time scale. Valid + + + + real + limits + limits + + 1.2 + + Efficiency factor of wind TKE generation in the Oberhuber + + + + real + limits + limits + + 0. + + Efficiency factor of TKE generation by momentum + + + + real + limits + limits + + .06 + .5 + 1.0 + + Efficiency factor for the restratification by mixed layer + + + + char + limits + limits + + UNSET + $DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx2v1_20130419.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx1v4_20170605.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx0.25v4_20170626.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/tidal_dissipation_tnx0.125v4_20221013.nc + + + Name of file containing tidal wave energy dissipation divided by + by bottom buoyancy frequency + + + + + real + limits + limits + + 0. + .4 + + Global factor applied to the energy input by near-intertial + + + + real + limits + limits + + .35 + + Fraction of near-inertial energy dissipated in the boundary + + + + real + limits + limits + + .5 + + Fraction of near-inertial energy dissipated locally beneath + + + + char + limits + limits + + jerlov + + Shortwave radiation absorption method. Valid methods: + + + + integer + limits + limits + + 3 + + Number indicating the Jerlov (1968) water type (i) + + + + char + limits + limits + + climatology + + Chlorophyll concentration option. Valid options: + + + + char + limits + limits + + unset + $DIN_LOC_ROOT/ocn/blom/bndcon/chlorophyll_concentration_tnx1v4_20170608.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/chlorophyll_concentration_tnx0.25v4_20170623.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/chlorophyll_concentration_tnx0.125v4_20221013.nc + + + Name of file containing chlorophyll concentration climatology + + + + + real + limits + limits + + 0. + + e-folding time scale (days) for SST relax., if 0 no relax. (f) + + + + real + limits + limits + + 0. + 60. + 6. + + e-folding time scale (days) for SSS relax., if 0 no relax. (f) + + + + real + limits + limits + + 1. + + Maximum mixed layer depth for e-folding SST relaxation (m) (f) + + + + real + limits + limits + + 1. + 1. + 10. + + Maximum mixed layer depth for e-folding SSS relaxation (m) (f) + + + + real + limits + limits + + 1.5 + + Max. absolute value of SST difference in relaxation (degC) (f) + + + + real + limits + limits + + .5 + + Max. absolute value of SSS difference in relaxation (psu) (f) + + + + logical + limits + limits + + .false. + + Apply diagnosed heat flux flag (l) + + + + logical + limits + limits + + .false. + + Apply diagnosed freshwater flux flag (l) + + + + logical + limits + limits + + .false. + + Diagnose heat flux flag (l) + + + + logical + limits + limits + + .false. + + Diagnose freshwater flux flag (l) + + + + logical + limits + limits + + .false. + .true. + + Balance the SSS relaxation (l) + + + + char + limits + limits + + unset + $DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx2v1_20130927.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx1v4_20170604.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx0.25v4_20170623.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/sss_clim_core_tnx0.125v4_20221013.nc + + Name of file containing SSS climatology used for relaxation (a) + Name of file containing SSS climatology used for relaxation + + + + + char + limits + limits + + none + + Source of wave fields. Valid source: 'none', 'param', 'extern' (a) + + + + logical + limits + limits + + .true. + .false. + + Smooth NorESM forcing (l) + + + + logical + limits + limits + + .false. + .true. + + Send precipitation/runoff factor to NorESM coupler (l) + + + + char + limits + limits + + unset + + Path to forcing fields in case of EXPCNF 'ben02clim' or + + + + integer + limits + limits + + 60 + + Global i-index of point diagnostics (i) + + + + integer + limits + limits + + 60 + + Global j-index of point diagnostics (i) + + + + logical + limits + limits + + .false. + + Conservation diagnostics flag (l) + + + + logical + limits + limits + + .false. + + Checksum diagnostics flag (l) + + + + integer + limits + limits + + 1 + + Restart frequency in days (30=1month,365=1year) (i) + + + + integer + limits + limits + + 0 + 1 + + Format of restart file (valid arguments are 0 for classic, + + + + integer + limits + limits + + 0 + + Compression flag for restart file (i) + + + + integer + limits + limits + + 0 + 1 + + 0 = netcdf, 1 = pnetcdf + + + + + + + + char + vcoord + vcoord + + $BLOM_VCOORD + + add desc + + + + char + vcoord + vcoord + + ppm + + add desc + + + + char + vcoord + vcoord + + monotonic + + add desc + + + + char + vcoord + vcoord + + non_oscillatory + + add desc + + + + char + vcoord + vcoord + + non_oscillatory + + add desc + + + + logical + vcoord + vcoord + + .false. + + add desc + + + + logical + vcoord + vcoord + + .false. + + add desc + + + + logical + vcoord + vcoord + + .true. + + add desc + + + + logical + vcoord + vcoord + + .false. + + add desc + + + + logical + vcoord + vcoord + + .true. + + add desc + + + + logical + vcoord + vcoord + + .false. + + add desc + + + + real + vcoord + vcoord + + 2.5 + + add desc + + + + real + vcoord + vcoord + + 1.08 + + add desc + + + + real + vcoord + vcoord + + .1 + + add desc + + + + integer + vcoord + vcoord + + 4 + + add desc + + + + integer + vcoord + vcoord + + 1 + + add desc + + + + + + + + char + diffusion + diffusion + + gm + + Eddy-induced transport parameterization method. Valid + + + + char + diffusion + diffusion + + "large scale" + + Type of Richardson number used in eddy diffusivity + + + + char + diffusion + diffusion + + smooth + step + step + + Method to estimate eddy diffusivity weight as a function of + + + + logical + diffusion + diffusion + + .false. + + If true, eddy diffusivity has a 2d structure (l) + + + + logical + diffusion + diffusion + + .true. + + Apply eddy mixing suppression away from steering level (l) + + + + real + diffusion + diffusion + + 0.85 + 0.5 + 0.5 + + Parameter c in Eden and Greatbatch (2008) parameterization (f) + + + + real + diffusion + diffusion + + 200. + + Parameter gamma in E. and G. (2008) param. (f) + + + + real + diffusion + diffusion + + 4000. + 4000.e2 + + Minimum eddy length scale in E. and G. (2008) param. (cm) (f) + + + + real + diffusion + diffusion + + 100. + 100.e4 + 0. + 0. + + Minimum diffusivity in E. and G. (2008) param. (cm**2/s) (f) + + + + real + diffusion + diffusion + + 1500. + 1500.e4 + 1000. + 1000.e4 + 1000. + 1000.e4 + 0. + 0. + + Maximum diffusivity in E. and G. (2008) param. (cm**2/s) (f) + + + + real + diffusion + diffusion + + 1. + + Factor relating the isopycnal diffusivity to the layer + + + + char + diffusion + diffusion + + unset + + Name of file containing topographic beta parameter (a)Name of file containing topographic beta parameter + + + + real + diffusion + diffusion + + 0. + + Linear scaling parameter for topographic rhines scale () (f) + + + + logical + diffusion + diffusion + + .false. + + If true, apply anisotropy correction to eddy diffusivity (l) + + + + logical + diffusion + diffusion + + .false. + + If true, then isopycnal/neutral diffusion will have 3D + + + + logical + diffusion + diffusion + + .false. + + If true, use the minimum of planetary and topographic beta + + + + real + diffusion + diffusion + + 1.2 + + Critical gradient richardson number for shear driven + + + + integer + diffusion + diffusion + + 2 + + Type of background diapycnal mixing. If bdmtyp=1 the + + + + real + diffusion + diffusion + + 5.e-8 + 5.e-4 + + Background diapycnal diffusivity times buoyancy frequency + + + + real + diffusion + diffusion + + 1.e-5 + .1 + + Background diapycnal diffusivity (cm**2/s) (f) + + + + logical + diffusion + diffusion + + .false. + .true. + + Make the background mixing latitude dependent according to + + + + real + diffusion + diffusion + + .006 + + Fraction of surface TKE that penetrates beneath mixed layer + + + + logical + diffusion + diffusion + + .true. + + If true, apply lateral smoothing of CVMix estimated + + + + char + diffusion + diffusion + + none + + Type of CVMix Langmuir turbulence parameterization. Valid + + + + char + diffusion + diffusion + + neutral + layer + layer + layer + + Type of lateral tracer eddy diffusion: Valid methods: + + + + + + + + char(2) + cwmod + cwmod + + '','' + 'Gibraltar','Gibraltar' + 'Gibraltar','Gibraltar' + + Array of geographical names of channels to be modified (a) + + + + char(2) + cwmod + cwmod + + 'unset','unset' + 'u','u' + 'u','u' + + Array of C grid cell edges to be modified. Valid options: + + + + integer(2) + cwmod + cwmod + + -999,-999 + 53,54 + 105,106 + + Array of grid cell i-indices (i) + + + + integer(2) + cwmod + cwmod + + -999,-999 + 137,137 + 273,273 + + Array of grid cell j-indices (i) + + + + real(2) + cwmod + cwmod + + 1.e36,1.e36 + 30.e3,30.e3 + 30.e3,30.e3 + + Array of modified grid cell widths (m) (f) + + + + + + + + char + secdia + secdia + + unset + $DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx2v1_20190826.dat + $DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx1v4_20190611.dat + $DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx0.25v4_20190612.dat + $DIN_LOC_ROOT/ocn/blom/grid/section_index_tnx0.125v4_20221013.dat + + + Name of file containing section specification for section + transport computation + + + + + + + + + + char(3) + diaphy + diaphy + + 'hd','hm','hy' + + tag used in file name (c10) + + + + integer(3) + diaphy + diaphy + + 1,30,365 + + average period in days + + + + integer(3) + diaphy + diaphy + + 30,30,365 + 1,30,365 + 30,30,365 + + how often to start a new file in days + + + + integer(3) + diaphy + diaphy + + 0,0,0 + + switch for compressed/uncompressed output + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 1,1,1 + + + netcdf format (valid arguments are 0 for classic, 1 for 64-bit + offset and 2 for netcdf4/hdf5 format) + + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + absolute wind speed [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + + surface albedo [] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + Barotropic mass streamfunction [kg s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + brine flux [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + brine plume depth [m] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + non-solar heat flux derivative [W m-2 K-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + evaporation [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + ice concentration [%] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + fresh water flux due to melting/freezing [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + ice thickness [m] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + heat flux due to melting/freezing [W m-2] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + snow depth [m] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + ice age [d] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer inertial kinetic energy tendency [kg s-3] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + Langmuir enhancement factor [] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + Surface layer averaged Langmuir number [] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + liquid precipitation [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + maximum mixed layer depth [m] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer depth [m] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + mixed layer thickness using "sigma-t" criterion [m] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + minimum mixed layer thickness using "sigma-t" criterion [m] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + maximum mixed layer thickness using "sigma-t" criterion [m] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer thickness squared using "sigma-t" criterion [m2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer TKE tendency related to friction velocity [kg s-3] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer TKE tendency related to near inertial mot. [kg s-3] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer TKE tendency related to buoyancy forcing [kg s-3] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer TKE tendency related to eddy restrat. [kg s-3] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer TKE tendency related to pot. energy change [kg s-3] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer TKE tendency related to kin. energy change [kg s-3] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + wind stress y-component [N m-2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + non-solar heat flux [W m-2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + bottom pressure [Pa] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + surface pressure [Pa] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + frozen runoff [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + liquid runoff [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + salt flux received by ocean [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + restoring salt flux received by ocean [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + bottom salinity [g kg-1] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + sea level [m] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + sea level squared [m2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + salt flux [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + solid precipitation [kg m-2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + mixed layer density [kg m-3] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + ocean surface salinity [g kg-1] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + ocean surface salinity squared [g2 kg-2] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + ocean surface temperature [degC] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + ocean surface temperature squared [degC2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + heat flux received by ocean [W m-2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + restoring heat flux received by ocean [W m-2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + short-wave heat flux [W m-2] + + + + integer(3) + diaphy + diaphy + + 4,4,0 + 4,4,0 + 0,0,0 + + 20C isoterm depth [m] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + momentum flux received by ocean x-component [N m-2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + momentum flux received by ocean y-component [N m-2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + bottom temperature [degC] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + ice temperature [degC] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + surface temperature [degC] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + barotropic velocity x-component [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + ice velocity x-component [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + friction velocity [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + friction velocity cubed [m3 s-3] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + surface Stokes drift x-componen [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + barotropic velocity y-component [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + ice velocity y-component [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + surface Stokes drift y-componen [m s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + wind stress x-component [N m-2] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + buoyancy frequency squared [s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + vertical diffusivity [log10(m2 s-1)|m2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + vertical momentum diffusivity [log10(m2 s-1)|m2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + vertical heat diffusivity [log10(m2 s-1)|m2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + vertical salt diffusivity [log10(m2 s-1)|m2 s-1] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + layer interface diffusivity [log10(m2 s-1)] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + isopycnal diffusivity [log10(m2 s-1)] + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,0 + 0,0,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,0,4 + 0,0,4 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + integer(3) + diaphy + diaphy + + 0,4,0 + 4,4,0 + 0,0,0 + + add desc + + + + + + + + char + merdia + merdia + + UNSET + $DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx2v1_20190826.nc + $DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx1v4_20190729.nc + $DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx0.25v4_20190612.nc + $DIN_LOC_ROOT/ocn/blom/grid/ocean_regions_tnx0.125v4_20221013.nc + + Name of file containing ocean region specification (a) + + + + char + merdia + merdia + + UNSET + $DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx2v1_20190826.dat + $DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx1v4_20190615.dat + $DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx0.25v4_20190701.dat + $DIN_LOC_ROOT/ocn/blom/grid/mertra_index_tnx0.125v4_20221013.dat + + Name of file containing zonal section specification for + + + + char(4) + merdia + merdia + + 'atlantic_arctic_ocean','atlantic_arctic_extended_ocean','indian_pacific_ocean','global_ocean' + + Array of region names for meridional overturning and flux + + + + integer(20) + merdia + merdia + + 2,4 + + Array of mask flags in ocean regions file to be included for region (1) + + + integer(20) + merdia + merdia + + 2,4,6,7,8,9 + + Array of mask flags in ocean regions file to be included for region (2) + + + integer(20) + merdia + merdia + + 3,5 + + Array of mask flags in ocean regions file to be included for region (3) + + + integer(20) + merdia + merdia + + 0 + + Array of mask flags in ocean regions file to be included for region (4) + + + + real(4) + merdia + merdia + + -34.,-34.,-34.,-90. + + Minimum latitude to be considered for each region (f) + + + + real(4) + merdia + merdia + + 90.,90.,90.,90. + + Maximum latitude to be considered for each region (f) + + + + + + + + real + bgcnml + bgcnml + + $CCSM_CO2_PPMV + + Atmospheric CO2 concentration [ppmv] + + + + char + bgcnml + bgcnml + + UNSET + $DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx2v1_20130506.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx1v4_20171107.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx0.25v4_20181004.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/dustdep_mhw2006_tnx0.125v4_20221013.nc + + 'File name (incl. full path) for iron (dust) deposition data' + + + + char + bgcnml + bgcnml + + UNSET + $DIN_LOC_ROOT/ocn/blom/bndcon/Annual_clim_swa_tnx1v4_20210415.nc + + File name (incl. full path) for swa climatology field (needed if bromoform scheme is activated) + + + + logical + bgcnml + bgcnml + + .false. + .true. + + Logical switch to activate riverine input + + + + char + bgcnml + bgcnml + + UNSET + $DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx2v1_20170915.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx1v4_20170820.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx0.25v4_20170821.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/river_nutrients_GNEWS2000_tnx0.125v4_20221013.nc + + File name (incl. full path) for riverine input data + + + + logical + bgcnml + bgcnml + + .false. + .true. + .false. + + Logical switch to activate N-deposition + + + + char + bgcnml + bgcnml + + '' + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_1850_CMIP6_tnx2v1_20180321.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_2000_CMIP6_tnx2v1_20200826.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_185001-201412_tnx2v1_20190702.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_1850_CMIP6_tnx1v4_20171106.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_2000_CMIP6_tnx1v4_20200826.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_185001-201412_tnx1v4_20180613.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_201501-210012-${BLOM_NDEP_SCENARIO}_tnx1v4_20191112.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_1850_CMIP6_tnx0.25v4_20190912.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_2000_CMIP6_tnx0.25v4_20200826.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_185001-201412_tnx0.25v4_20190705.nc + $DIN_LOC_ROOT/ocn/blom/bndcon/ndep_1850_CMIP6_tnx0.125v4_20221013.nc + + File name (incl. full path) for atmopheric N-deposition data + + + + logical + bgcnml + bgcnml + + .false. + + add desc + + + + logical + bgcnml + bgcnml + + .false. + .true. + + Logical switch to activate sediment spin-upLogical switch to activate sediment spin-up + + + + integer + bgcnml + bgcnml + + -1 + $HAMOCC_SEDSPINUP_YR_START + + Start year for sediment spinup + + + + integer + bgcnml + bgcnml + + -1 + $HAMOCC_SEDSPINUP_YR_END + + End year for sediment spinup + + + + integer + bgcnml + bgcnml + + -1 + + Number of subcyles per time-step for sediment spinup + + + + char + bgcnml + bgcnml + + $DIN_LOC_ROOT/ocn/blom/inicon/glodapv2_Ct_preind_OMIPinit_20171107.nc + + add desc + + + + char + bgcnml + bgcnml + + $DIN_LOC_ROOT/ocn/blom/inicon/glodapv2_At_OMIPinit_20171107.nc + + add desc + + + + char + bgcnml + bgcnml + + $DIN_LOC_ROOT/ocn/blom/inicon/woa13_phosphate_OMIPinit_20171107.nc + + add desc + + + + char + bgcnml + bgcnml + + $DIN_LOC_ROOT/ocn/blom/inicon/woa13_oxygen_OMIPinit_20171107.nc + + add desc + + + + char + bgcnml + bgcnml + + $DIN_LOC_ROOT/ocn/blom/inicon/woa13_nitrate_OMIPinit_20171107.nc + + add desc + + + + char + bgcnml + bgcnml + + $DIN_LOC_ROOT/ocn/blom/inicon/woa13_silicate_OMIPinit_20171107.nc + + add desc + + + + char + bgcnml + bgcnml + + '' + $DIN_LOC_ROOT/ocn/blom/inicon/d13C_permil_20180609.nc + + add desc + + + + char + bgcnml + bgcnml + + '' + $DIN_LOC_ROOT/ocn/blom/inicon/d14C_permil_20180609.nc + + add desc + + + + logical + bgcnml + bgcnml + + .false. + + add desc + + + + char + bgcnml + bgcnml + + '' + + + File name (incl. full path) for surface PI pH input data. + + + + + logical + bgcnml + bgcnml + + .false. + + add desc + + + + char + bgcnml + bgcnml + + '' + + + File name (incl. full path) for sediment porosity + + + + + + + + + + char + bgcoafx + bgcoafx + + '' + + Name of alkalinization scenario ('const', 'ramp', or 'file') + + + + char + bgcoafx + bgcoafx + + '' + + + Full path of the input file for the alkalinization scenario 'file' + + + + + real + bgcoafx + bgcoafx + + 0.135 + + Pmol alkalinity/yr added in 'const' or 'ramp' scenarios + + + + real + bgcoafx + bgcoafx + + 70.0 + + Max latitude where alkalinity is added in 'const' or 'ramp' scenarios + + + + real + bgcoafx + bgcoafx + + -60.0 + + Min latitude where alkalinity is added in 'const' or 'ramp' scenarios + + + + real + bgcoafx + bgcoafx + + 2025 + + Start year for ramp up in 'ramp' scenario + + + + real + bgcoafx + bgcoafx + + 2035 + + End year for ramp up in 'ramp' scenario + + + + + + + + real + bgcparams + bgcparams + + 4.e-8 + + phytoplankton parameter: kmol/m3 - i.e. 0.04 mmol P/m3 half saturation constant + + + + real + bgcparams + bgcparams + + 0.004 + + phytoplankton parameter: 1/d -mortality rate of phytoplankton + + + + real + bgcparams + bgcparams + + 0.005 + + phytoplankton parameter: 1/d - nitrogen fixation rate by blue green algae (cyanobacteria) + + + + real + bgcparams + bgcparams + + 8.e-8 + + zooplankton parameter: kmol/m3 - i.e. 0.08 mmol P/m3 half saturation constant + + + + real + bgcparams + bgcparams + + 1.2 + + zooplankton parameter: 1/d -grazing rate + + + + real + bgcparams + bgcparams + + 3.e6 + + zooplankton parameter: 1/d -mortality rate + + + + real + bgcparams + bgcparams + + 0.04 + + zooplankton parameter: 1/d -exudation rate + + + + real + bgcparams + bgcparams + + 0.06 + + zooplankton parameter: 1/d -excretion rate + + + + real + bgcparams + bgcparams + + 0.95 + + zooplankton parameter: fraction of mortality as PO_4 + + + + real + bgcparams + bgcparams + + 0.6 + 0.7 + 0.5 + + zooplankton parameter: dimensionless fraction -assimilation efficiency + + + + real + bgcparams + bgcparams + + 0.8 + 0.85 + 0.9 + + zooplankton parameter: dimensionless fraction -fraction of grazing egested + + + + real + bgcparams + bgcparams + + 5.e-6 + + shell production parameter: kmol/m3 - i.e. 4.0 mmol Si/m3 half saturation constant + + + + real + bgcparams + bgcparams + + 40. + 33. + 14. + + shell production parameter: calcium carbonate to organic phosphorous production ratio + + + + real + bgcparams + bgcparams + + 30. + 45. + 10.5 + + shell production parameter: + + + + real + bgcparams + bgcparams + + 0.004 + + remineralization and dissolution parameter: 1/d -remineralization rate (of DOM) + + + + real + bgcparams + bgcparams + + 0.025 + + remineralization and dissolution parameter: 1/d Aerob remineralization rate detritus + + + + real + bgcparams + bgcparams + + 0.003 + + remineralization and dissolution parameter: 1/d Dissolution rate for opal + + + + real + bgcparams + bgcparams + + 0.01 + + remineralization and dissolution parameter: 1/d Remineralization rate of detritus on N2O + + + + real + bgcparams + bgcparams + + 0.005 + + remineralization and dissolution parameter: 1/d Remineralization rate for sulphate reduction + + + + real + bgcparams + bgcparams + + 0.6 + + Dust deposition and iron solubility parameter: factor introduced to tune deposition/solubility + + + + + + + + + + + + + + + real + bgcparams + bgcparams + + 5. + + Sinking parameter: m/d Sinking speed of detritus iris : 5. + + + + real + bgcparams + bgcparams + + 30. + + Sinking parameter: m/d Sinking speed of CaCO3 shell material + + + + real + bgcparams + bgcparams + + 30. + + Sinking parameter: m/d Sinking speed of opal iris : 60 + + + + real + bgcparams + bgcparams + + 1. + + Sinking parameter: m/d minimum sinking speed + + + + real + bgcparams + bgcparams + + 60. + + Sinking parameter: m/d maximum sinking speed + + + + + + + + + + + + + + + + + + + char(3) + diabgc + diabgc + + 'hbgcd','hbgcm','hbgcy' + + add desc + + + + integer(3) + diabgc + diabgc + + 1,30,365 + + add desc + + + + integer(3) + diabgc + diabgc + + 30,30,365 + 1,30,365 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 1,1,1 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,1,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Phosphorus (po4) [mol P m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Oxygen (o2) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Dissolved iron (dfe) [mol Fe m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Nitrate (no3) [mol N m-3] + + + + integer(3) + diabgc + diabgc + + 4,2,2 + 4,2,2 + 0,0,0 + + Natural alkalinity (nattalk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 4,2,2 + 4,2,2 + 0,0,0 + + Dissolved carbon (dissic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 4,2,2 + 4,2,2 + 0,0,0 + + Phytoplankton (phyc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + pH (ph) [-log10([h+])] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Export production (epc100) [mol C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Si export production (epsi100) [mol Si m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Ca export production (epcalc100) [mol Ca m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Piston velocity (kwco2) [m s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Piston velocity times solubility (kwco2*kh; moist air) [m s-1 mol kg-1 uatm-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Piston velocity times solubility (kwco2*kh; moist air) [m s-1 mol kg-1 uatm-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + CO2 solubility under moist air assumption (kh) [mol kg-1 atm-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural surface PCO2 (spco2) [uatm] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Surface PCO2 under moist air assumption [uatm] + + + + integer(3) + diabgc + diabgc + + 4,2,2 + 4,2,2 + 0,0,0 + + Downward CO2 flux (co2fxd) [kg C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 4,2,2 + 4,2,2 + 0,0,0 + + Upward CO2 flux (co2fxu) [kg C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Oxygen flux (fgo2) [mol O2 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Nitrogen flux (fgn2) [mol N2 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 4,2,2 + 0,0,0 + + Nitrous oxide flux [mol N2O m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + DMS flux (dmsflux) [mol DMS m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + surface DMS concentration (dms) [mol DMS m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + DMS production (dmsprod) [mol DMS m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + DMS bacterial consuption (dms_bac) [mol DMS m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + DMS decomposition by UV (dms_uv) [mol DMS m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Atmospheric CO2 (atmco2) [ppm] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Atmospheric O2 (atmo2) [ppm] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Atmospheric N2 (atmn2) [ppm] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural dissolved carbon (natdissic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural alkalinity (nattalk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural pH (natph) [-log10([h+])] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural surface PCO2 (spco2) [uatm] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural net CO2 flux (natco2fx) [kg C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 0,2,2 + 0,0,0 + + Downward 13CO2 flux (co213fxd) [kg C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 0,2,2 + 0,0,0 + + Upward 13CO2 flux (co213fxu) [kg C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Downward 14CO2 flux (co214fxd) [kg C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Upward 14CO2 flux (co214fxu) [kg C m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + CFC11 flux [mol CFC11 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + CFC12 flux [mol CFC12 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + SF6 flux [mol SF6 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 4,2,2 + 4,2,2 + 0,0,0 + + Primary production (pp) [mol C m-3 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Vertically integrated nitrogen fixation + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Vertically integrated denitrification + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 0,2,2 + 4,2,2 + + Nitrogen deposition flux [mol N m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 0,0,0 + 0,0,0 + + AMELIST FOR DIAGNOSTIC iHAMOCC OUTPUT + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 4,2,2 + 0,0,0 + + Phosphorus (po4) [mol P m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,4 + 4,0,4 + 0,0,0 + + Oxygen (o2) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Dissolved iron (dfe) [mol Fe m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + CaCO3 shells (calc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Opal shells (opal) [mol Si m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Carbonate ions (co3) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 0,0,0 + 0,0,0 + + Nitrous oxide concentration [mol N2O m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Calcite saturation state (omegac) [1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural aragonite saturation state (natomegaa) [1] + + + + integer(3) + diabgc + diabgc + + 0,0,4 + 4,0,4 + 0,0,0 + + preformed oxygen (p_o2) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,4 + 4,0,4 + 0,0,0 + + Saturated oxygen (satoxy) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + preformed alkalinity (p_talk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + preformed DIC (p_dic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + saturated DIC (dic_sat) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural dissolved carbon (natdissic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural alkalinity (nattalk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural carbonate ion concentration (natco3) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural CaCO3 shells (natcalc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural pH (natph) [-log10([h+])] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural calcite saturation state (natomegac) [1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Natural aragonite saturation state (natomegaa) [1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Dissolved C13 (dissic13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + Dissolved C14 (dissic14) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + at-depth variable sediment porosity (as opposed to default: only depth) + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + delta 14C of DIC [1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + Delta 14C of DIC [1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + Particulate organic carbon 13 (detoc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + Dissolved organic carbon 13 (dissoc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + Particulate inorganic carbon 13 (calc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + 13C of phytoplankton biomass (phyc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + 13C of zootoplankton biomass (zooc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + CFC11 concentration [mol CFC11 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + CFC12 concentration [mol CFC12 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + SF6 concentration [mol SF6 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + index of point diagnostics (i) + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + Mass sinking velocity (aggregate scheme) [m d-1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 0,0,2 + 0,0,0 + + Number sinking velocity (aggregate scheme) [m d-1] + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 0,0,0 + 0,0,0 + + Epsilon exponent (aggregate scheme) [1] + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 0,0,0 + 0,0,0 + + Average particle size (aggregate scheme) + + + + integer(3) + diabgc + diabgc + + 0,0,2 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 0,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Phytoplankton (phyc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Zooplankton (zooc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Dissolved organic carbon (dissoc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Primary production (pp) [mol C m-3 s-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Phosphorus (po4) [mol P m-3] + + + + integer(3) + diabgc + diabgc + + 0,4,4 + 4,4,4 + 0,0,0 + + Oxygen (o2) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + eposition + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Nitrate (no3) [mol N m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Alkalinity (talk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Silicate (si) [mol Si m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Dissolved carbon (dissic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Detrius (detoc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + CaCO3 shells (calc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Opal shells (opal) [mol Si m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + ] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 4,2,2 + 0,0,0 + + Nitrous oxide concentration [mol N2O m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Calcite saturation state (omegac) [1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Aragonite saturation state (omegaa) [1] + + + + integer(3) + diabgc + diabgc + + 0,4,4 + 4,4,4 + 0,0,0 + + preformed oxygen (p_o2) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,4,4 + + Saturated oxygen (satoxy) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + preformed phosphate (p_po4) [mol PO4 m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + preformed alkalinity (p_talk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + preformed DIC (p_dic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + saturated DIC (dic_sat) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural dissolved carbon (natdissic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural alkalinity (nattalk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural carbonate ion concentration (natco3) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural CaCO3 shells (natcalc) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural pH (natph) [-log10([h+])] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural calcite saturation state (natomegac) [1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Natural aragonite saturation state (natomegaa) [1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Dissolved C13 (dissic13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Dissolved C14 (dissic14) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + delta 13C of DIC [1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Particulate organic carbon 13 (detoc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Dissolved organic carbon 13 (dissoc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Particulate inorganic carbon 13 (calc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + 13C of phytoplankton biomass (phyc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + 13C of zootoplankton biomass (zooc13) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + CFC11 concentration [mol CFC11 m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + CFC12 concentration [mol CFC12 m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + SF6 concentration [mol SF6 m-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Number of sinking particles (aggregate scheme,nos) [cm-3] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Mass sinking velocity (aggregate scheme) [m d-1] + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + Number sinking velocity (aggregate scheme) [m d-1] + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 0,0,0 + 0,0,0 + + Epsilon exponent (aggregate scheme) [1] + + + + integer(3) + diabgc + diabgc + + 0,0,0 + 0,0,0 + 0,0,0 + + Average particle size (aggregate scheme) + + + + integer(3) + diabgc + diabgc + + 0,2,2 + 4,2,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + add desc + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + sediment - water-column diffusive flux of oxygen [mol O2 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + sediment - water-column diffusive flux of N2 [mol N2 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + sediment - water-column diffusive flux of nitrate [mol NO3 m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + sediment - water-column diffusive flux of silica [mol Si m-2 s-1] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (powdic) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (powalk) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (powpho) [eq m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (powox) [mol O2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (pown2) [mol N2 m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (powno3)[mol N m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (powsi) [mol Si m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (ssso12) [mol m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (ssssil) [mol Si m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (sssc12) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (ssster) [mol m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (ssso12) [mol m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (ssssil) [mol Si m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (sssc12) [mol C m-3] + + + + integer(3) + diabgc + diabgc + + 0,0,2 + 2,0,2 + 0,0,0 + + (ssster) [mol m-3] + + + diff --git a/cime_config/ocn_in_paramgen.py b/cime_config/ocn_in_paramgen.py new file mode 100644 index 00000000..2b51efcd --- /dev/null +++ b/cime_config/ocn_in_paramgen.py @@ -0,0 +1,1703 @@ +""" +Wrapper-class for the BLOM ParamGen tool, and associated methods +needed to generated the "ocn_in" Fortran namelist file. + +To run doctests on this file: python -m doctest ocn_in_paramgen.py +""" + +#---------------------------------------- +# Import generic python libraries/modules +#---------------------------------------- + +import os +import os.path +import sys +import re +from collections import OrderedDict + +#---------------- +# Import ParamGen +#---------------- + +file_path = os.path.abspath(__file__) +directory = os.path.dirname(file_path) +sys.path.append(os.path.join(directory, "ParamGen")) +#pylint: disable=wrong-import-position +from paramgen import ParamGen +#pylint: enable=wrong-import-position + +#Set of single and double quotes used by "_check_string_quotes" function: +_QUOTE_SET = {"'", '"'} + +#Regular expression used by "remove_user_nl_comment" function: +_QUOTE_REGEX = re.compile(r"\".*?\"|'.*?'") + +#Regular expression used by the "write" and "append_user_nl_file" +#methods to determine if the variable is an array, and what +#the dimensions of the array are: +_ARRAY_TYPE_REGEX = re.compile(r"[(][ ]*([0-9 ,]+)[ ]*[)]") + +#Regular expression used to determine array indices in +#"find_arr_indices" function: +_ARR_INDEX_REGEX = re.compile(r"\((.+?)\)") + +################################################################ + +class OcnInParamGenError(ValueError): + """Class used to handle ocn_in ParamGen errors + (e.g., log user errors without backtrace)""" + +################################################################ +#HELPER FUNCTIONS +################################################################ + +def _is_nml_logical_true(varname, var_val): + + """ + Checks if a "logical" XML namelist value is true or + false. + ---------- + varname -> The name of the variable being checked + var_val -> The value of the variable being checked + + Returns a boolean that matches the value of the + input logical. + + doctests: + + 1. Check that a True value returns true: + >>> _is_nml_logical_true("test", True) + True + + 2. Check that a "true" value returns true: + >>> _is_nml_logical_true("test", "true") + True + + 3. Check that a ".true." value returns true: + >>> _is_nml_logical_true("test", ".true.") + True + + 4. Check that a "1" value returns true: + >>> _is_nml_logical_true("test", "1") + True + + 5. Check that a 1 (integer) value returns true: + >>> _is_nml_logical_true("test", 1) + True + + 6. Check that a False value returns false: + >>> _is_nml_logical_true("test", False) + False + + 7. Check that a "FALSE" value returns false: + >>> _is_nml_logical_true("test", "FALSE") + False + + 8. Check that a ".False." value returns false: + >>> _is_nml_logical_true("test", ".False.") + False + + 9. Check that a "0" value returns false: + >>> _is_nml_logical_true("test", "0") + False + + 10. Check that a 0 (integer) value returns false: + >>> _is_nml_logical_true("test", 0) + False + + 11. Check that a bad string value returns the correct error: + >>> _is_nml_logical_true("test", "this_wont_work") # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError:... + XML namelist logical variable, 'test', must have a value of true, false, 1, or 0, not 'this_wont_work' + + 12. Check that a bad integer value returns the correct error: + >>> _is_nml_logical_true("test", 3) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError:... + XML namelist logical variable, 'test', must have a value of true, false, 1, or 0, not 3 + + 13. Check that an unsupported type returns an error: + >>> _is_nml_logical_true("test", 13.03) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError:... + XML namelist variable 'test' must have a value that is either a boolean, string, or integer, not float. + + """ + + if isinstance(var_val, bool): + return var_val + #End if + if isinstance(var_val, str): + if var_val.lower() in {"true", ".true.", "1"}: + return True + #End if + if var_val.lower() in {"false", ".false.", "0"}: + return False + #End if + + #Raise error if no match was found: + emsg = f"\nXML namelist logical variable, '{varname}'" + emsg += ", must have a value of true, false, 1, or 0, not" + emsg += f" '{var_val}'" + raise OcnInParamGenError(emsg) + #End if + + if isinstance(var_val, int): + if var_val == 1: + return True + #End if + if var_val == 0: + return False + #End if + + #Raise error if no match was found: + emsg = f"\nXML namelist logical variable, '{varname}'" + emsg += ", must have a value of true, false, 1, or 0, not" + emsg += f" {var_val}" + raise OcnInParamGenError(emsg) + #End if + + #Type is un-recognizeda, so raise an error: + emsg = f"\nXML namelist variable '{varname}' must" + emsg += " have a value that is either a boolean, string, or integer," + emsg += f" not {type(var_val).__name__}." + raise OcnInParamGenError(emsg) + +##### + +def remove_user_nl_comment(user_string, comment_delim="!"): + + """ + Searches a one-line input string for a comment delimiter, + and then returns the string with all text after the delimiter + removed. + ---------- + user_string -> String that will be searched and processed for comments + comment_delim -> Optional variable that sets the character type being used + as a comment delimiter. Defaults to the standard "!" fortran comment. + + Returns the input string, but with any commented text removed. + + doctests: + + 1. Check that a string with no comment delimiters returns full string: + >>> remove_user_nl_comment("bananas") + 'bananas' + + 2. Check that a string with no comments outside quotes returns full string: + >>> remove_user_nl_comment(" '!ban!anas!' ") + " '!ban!anas!' " + + 3. Check that a string with no quotes but a comment returns string with no comment: + >>> remove_user_nl_comment("bananas !But not apples") + 'bananas ' + + 4. Check that a string with quotes and a comment returns string sans comment: + >>> remove_user_nl_comment(" 'bananas' !But not apples") + " 'bananas' " + + 5. Check that a string with a quoted comment and real comments returns proper string: + >>> remove_user_nl_comment(" '!ba!na!nas!' !But not apples") + " '!ba!na!nas!' " + + 6. Check that a string with a quoted comment and a real comment with multiple delimiters + returns the proper string: + >>> remove_user_nl_comment(" '!bananas!' !But not! apples!") + " '!bananas!' " + + 7. Check that a string with a quoted comment and a commented quote returns the proper string: + >>> remove_user_nl_comment(' "!bananas" !"But not apples" ') + ' "!bananas" ' + + 8. Check that a string with quotes inside quotes and multiple delimiters returns + the proper string: + >>> remove_user_nl_comment(''' "!bana'!'anas""other''fruit!" !But not '!Apples!' ''') + ' "!bana\\'!\\'anas""other\\'\\'fruit!" ' + + 9. Check that an array of strings returns the proper string: + >>> remove_user_nl_comment(" 'bananas', 'apples', 'kiwis' ") + " 'bananas', 'apples', 'kiwis' " + + 10. Check that an array of strings with a comment returns the proper string: + >>> remove_user_nl_comment(" 'bananas', 'apples', 'kiwis', !, and coconuts") + " 'bananas', 'apples', 'kiwis', " + + 11. Check that an array of of strings with comment delimiters and an actual comment + returns the proper string: + >>> remove_user_nl_comment(' , "!bananas", "app!les", "kiwis!", !And "Coconuts"!') + ' , "!bananas", "app!les", "kiwis!", ' + + 12. Check that a line with no comments or strings returns the proper string: + >>> remove_user_nl_comment('5') + '5' + + 13. Check that a line with a comment but no internal strings returns the proper string: + >>> remove_user_nl_comment(' .true. !And not .false.') + ' .true. ' + + 14. Check that an array of values with no comment returns the proper string: + >>> remove_user_nl_comment('13.0d0, 15.0d0, 1100.35d0') + '13.0d0, 15.0d0, 1100.35d0' + + 15. Check that an array of values with a comment returns the proper string: + >>> remove_user_nl_comment('13.0d0,! 15.0d0, 1100.35d0') + '13.0d0,' + + 16. Check that a line that only contains a comment returns an empty string: + >>> remove_user_nl_comment('! bananas and 13.0d0 5 .true. !@$#%*?') + '' + + 17. Check that a line with an alternative comment delimiter returns the proper string: + >>> remove_user_nl_comment('bananas #and 13.0d0 5 .true. !@$#%*?', comment_delim='#') + 'bananas ' + + 18. Check that some more unusual strings are handled correctly + >>> remove_user_nl_comment("'Isn''t it a nice day'") + "'Isn''t it a nice day'" + >>> remove_user_nl_comment("'Isn''t it a nice day' !comment") + "'Isn''t it a nice day' " + >>> remove_user_nl_comment("'Isn!''!t it a nice! day'") + "'Isn!''!t it a nice! day'" + >>> remove_user_nl_comment("'Isn!''!t it a nice! day' ! comment") + "'Isn!''!t it a nice! day' " + >>> remove_user_nl_comment('''"This is 'one' string"''') + '"This is \\'one\\' string"' + >>> remove_user_nl_comment('''"This is 'one' string" !comment''') + '"This is \\'one\\' string" ' + >>> remove_user_nl_comment("'This is \\"one\\" string'") + '\\'This is "one" string\\'' + >>> remove_user_nl_comment("'This is \\"one\\" string'! comment") + '\\'This is "one" string\\'' + >>> remove_user_nl_comment("'This! is \\"!one\\"! string'! comment") + '\\'This! is "!one"! string\\'' + """ + + #Create empty set for comment-delimiting indices: + comment_delim_indices = set() + + #Search for all comment delimiters: + for char_idx, char in enumerate(user_string): + if char == comment_delim: + #Add character index to set: + comment_delim_indices.add(char_idx) + #End if + #End for + + #If no comments are present, then return string as-is: + if not comment_delim_indices: + return user_string + #End if + + #Next, check if any single or double quotes are present: + if not "'" in user_string and not '"' in user_string: + #If no quotes, then cut-off string at first delimiter: + return user_string[:sorted(comment_delim_indices)[0]] + #End if + + #Create empty set for all character indices inside quotes: + quote_text_indices = set() + + #Search for all text within quotes: + quoted_text_matches = _QUOTE_REGEX.finditer(user_string) + + #Loop over all matches: + for quote_match in quoted_text_matches: + #Extract min/max indices of match: + index_span = quote_match.span(0) + #Add all indices to set: + for index in range(index_span[0], index_span[1]): + quote_text_indices.add(index) + #End for + #End for + + #Find all comment delimiters outside of quotes: + non_quote_comment = comment_delim_indices.difference(quote_text_indices) + + if not non_quote_comment: + #All comment delimiters are within quotes, + #so return string as-is: + return user_string + #End if + + #Find first comment delimiter outside of quotes. + #Everything to the right of it is part of the comment: + return user_string[:sorted(non_quote_comment)[0]] + +##### + +def user_nl_str_to_int(string, var_name): + + """ + Checks if a string can be converted + into an integer, and if not reports + the relevant error. This function + is only used in the "get_user_nl_var_array_info" + function below. + ---------- + string -> string to convert to integer. + var_name -> name of the array variable + associated with the string. + + doctests: + + 1. Check that a string with an integer can be + converted properly: + >>> user_nl_str_to_int("5", "banana") + 5 + + 2. Check that a string with a float fails with + the correct error: + >>> user_nl_str_to_int("5.2", "banana") # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError:... + Invalid array index entry '5.2' used for variable 'banana' in 'user_nl_blom'. + + 3. Check that a string with a non-number fails with + the correct error: + >>> user_nl_str_to_int("a", "banana") # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError:... + Invalid array index entry 'a' used for variable 'banana' in 'user_nl_blom'. + + """ + + #Attempt the conversion of the string to an integer: + try: + integer_val = int(string) + except ValueError as verr: + emsg = f"\nInvalid array index entry '{string}' " + emsg += f"used for variable '{var_name}' in 'user_nl_blom'." + raise OcnInParamGenError(emsg) from verr + #End except + + #Return relevant integer value: + return integer_val + +##### + +def check_dim_index(var_name, index_val, dim_size): + + """ + Checks that the user-specified index for the given + variables is within the dimension size limit as + specified in the namelist definition file. + ---------- + var_name -> Name of the array variable + associated with the string. + index_val -> Index value provided by user + dim_size -> Maximum variable dimension size. + + doctests: + + 1. Check that an in-bounds index value + returns nothing: + >>> check_dim_index("banana", 5, 15) + + 2. Check that an index value that is + too small returns the proper error: + >>> check_dim_index("banana", 0, 15) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError:... + Variable 'banana' has index 0 in 'user_nl_blom', which is less than one (1), the minimal index value allowed. + + 3. Check that an index value that is + too large returns the proper error: + >>> check_dim_index("banana", 20, 15) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError:... + Variable 'banana' has index 20 in 'user_nl_blom', which is greater than the max dimension size of 15 + + """ + + #Make sure index is greater than zero: + if index_val <= 0: + emsg = f"\nVariable '{var_name}' has index {index_val}" + emsg += " in 'user_nl_blom', which is less than one (1)," + emsg += " the minimal index value allowed." + raise OcnInParamGenError(emsg) + #End if + + #Make sure index is not greater than max value: + if index_val > dim_size: + emsg = f"\nVariable '{var_name}' has index {index_val}" + emsg += " in 'user_nl_blom', which is greater than the" + emsg +=f" max dimension size of {dim_size}" + raise OcnInParamGenError(emsg) + #End if + +##### + +def parse_dim_spec(var_name, array_spec_text, dim_size): + """ + Given the text of a single array dimension specification, + return the range of values specified by the specification or + raise an Exception if an error is detected. + is the variable name and is used for error messages + is the text representation of the array spec + is the size of that rank in + + 1. Check that a single, legal index returns the correct single value + >>> parse_dim_spec('banana', '5', 10) + [5] + + 2. Check that a single, out-of-bounds index generates the proper error + >>> parse_dim_spec('banana', '15', 10) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: + Variable 'banana' has index 15 in 'user_nl_blom', which is greater than the max dimension size of 10 + >>> parse_dim_spec('banana', '0', 10) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: + Variable 'banana' has index 0 in 'user_nl_blom', which is less than one (1), the minimal index value allowed. + >>> parse_dim_spec('banana', '-2', 10) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: + Variable 'banana' has index -2 in 'user_nl_blom', which is less than one (1), the minimal index value allowed. + + 3. Check that a legal range returns the correct list of indices + >>> parse_dim_spec('banana', '5:9', 10) + [5, 6, 7, 8, 9] + >>> parse_dim_spec('banana', ':9', 10) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> parse_dim_spec('banana', ':', 10) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + >>> parse_dim_spec('banana', '6:', 10) + [6, 7, 8, 9, 10] + + 4. Check that an out-of-bounds range returns the correct list + >>> parse_dim_spec('banana', '0:2', 10) + [1, 2] + >>> parse_dim_spec('banana', '7:11', 10) + [7, 8, 9, 10] + >>> parse_dim_spec('banana', '-1:11', 10) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + 5. Check that an empty range returns an empty list + >>> parse_dim_spec('banana', '5:1', 10) + [] + + 6. Check that a legal range with a stride returns the correct list + >>> parse_dim_spec('banana', '5:9:2', 10) + [5, 7, 9] + >>> parse_dim_spec('banana', ':9:3', 10) + [1, 4, 7] + >>> parse_dim_spec('banana', '::3', 10) + [1, 4, 7, 10] + >>> parse_dim_spec('banana', '6:', 10) + [6, 7, 8, 9, 10] + >>> parse_dim_spec('banana', '9:1:-3', 10) + [9, 6, 3] + + 7. Check that a mismatched stride returns an empty list + >>> parse_dim_spec('banana', '9:5:2', 10) + [] + >>> parse_dim_spec('banana', '5:9:-2', 10) + [] + >>> parse_dim_spec('banana', ':9:-3', 10) + [] + >>> parse_dim_spec('banana', '::-2', 10) + [] + >>> parse_dim_spec('banana', '6::-1', 10) + [] + >>> parse_dim_spec('banana', '9:1:3', 10) + [] + + 8. Check that a missing stride value generates an error + >>> parse_dim_spec('banana', '2::', 10) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: + Two colons were provided for variable 'banana' in 'user_nl_blom', \ + but no stride value was provided. + Please provide either a stride value, or remove the extra colon. + """ + array_dims = [x.strip() for x in array_spec_text.split(':')] + if len(array_dims) > 3: + #Not sure what to do with three or more colons, so die here: + emsg = f"Variable '{var_name}' has {len(array_dims) - 1} colons (:) " + emsg += "listed in its dimension indexing in 'user_nl_blom'." + emsg += " This is not a valid Fortran array section specification." + raise OcnInParamGenError(emsg) + #End if + # Defaults + arr_beg = 1 + arr_end = dim_size + arr_stride = 1 + # Override start index? + if array_dims[0]: + arr_beg = user_nl_str_to_int(array_dims[0], var_name) + # end if + # Override end index? + if len(array_dims) > 1: + if array_dims[1].strip(): + arr_end = user_nl_str_to_int(array_dims[1], var_name) + #End if (no else, blank means use default) + else: + # We only need to check this if it is only a single index + check_dim_index(var_name, arr_beg, dim_size) + # For a single index, the end is the same as the beginning + arr_end = arr_beg + #End if + # Override stride? + if len(array_dims) > 2: + if array_dims[2]: + arr_stride = user_nl_str_to_int(array_dims[2], var_name) + if arr_stride == 0: + emsg = f"Variable '{var_name}' has a stride of zero " + emsg += "listed in its dimension indexing in 'user_nl_blom'." + emsg += " This is not a valid Fortran stride." + raise OcnInParamGenError(emsg) + #End if + else: + emsg = f"Two colons were provided for variable '{var_name}'" + emsg += " in 'user_nl_blom', but no stride value was provided." + emsg += "\nPlease provide either a stride value, or remove the " + emsg += "extra colon." + raise OcnInParamGenError(emsg) + #End if + #End if (no else, just use default stride) + # Now, create the set of entries + # We need to modify the end to make the range function compatible with + # how Fortran uses it + arr_end += int(arr_stride / abs(arr_stride)) + return [x for x in list(range(arr_beg, arr_end, arr_stride)) if + ((x >= 1) and (x <= dim_size))] + +##### + +def _check_string_quotes(var_name, var_val): + + """ + Checks if a string is inside closed quotes, + i.e. has both a starting and ending quote + of the same type. This function also + raises an error if there are quotes but + they aren't closed: + + doctests: + + 1. Check that a string with single quotes returns "True": + >>> _check_string_quotes("Apple", "'Banana'") + True + + 2. Check that a string with double quotes returns "True": + >>> _check_string_quotes("Apple", '"Banana"') + True + + 3. Check that a string without quotes returns "False": + >>> _check_string_quotes("Apple", "Banana") + False + + 4. Check that a string with mis-matching quote types raises + the appropriate error: + >>> _check_string_quotes("Apple", ''' "Banana' ''') # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: Namelist entry 'Apple' is of type character but its input value: + "Banana' + has mis-matched quotes. Please fix. + + 5. Check that a string with a missing ending quote type raises + the appropriate error: + >>> _check_string_quotes("Apple", "'Banana") # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: Namelist entry 'Apple' is of type character but its input value: + 'Banana + has mis-matched quotes. Please fix. + + 5. Check that a string with a missing starting quote type raises + the appropriate error: + >>> _check_string_quotes("Apple", 'Banana"') # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: Namelist entry 'Apple' is of type character but its input value: + Banana" + has mis-matched quotes. Please fix. + + """ + + #Make sure variable has been stripped: + var_val_strip = var_val.strip() + + # If string is empty, just return + if var_val_strip is '': + return True + + #Set error message (just in case): + emsg = f"Namelist entry '{var_name}' is of type character" + emsg += " but its input value:" + emsg += f"\n{var_val}\n" + emsg += "has mis-matched quotes. Please fix." + + #Check if starting and ending quotes exist and match: + if var_val_strip[0] in _QUOTE_SET: + if var_val_strip[0] == var_val_strip[-1]: + #String is inside closed quotes: + return True + #End if + + #Starting and ending quotes don't match, + #so raise an error: + raise OcnInParamGenError(emsg) + #End if + + #Check if there are ending quotes as well: + if var_val_strip[-1] in _QUOTE_SET: + #No starting quotes, raise an error: + raise OcnInParamGenError(emsg) + #End if + + #String is not inside quotes: + return False + +##### + +def _get_nml_value_str(var_name, var_type, var_val): + + """ + Converts namelist variable inputs into their + correct Fortran namelist value format + ---------- + var_name -> Variable name (used for error message) + var_type -> Variable type to convert to (logical, integer, real, character) + var_val -> Variable value to convert + + returns the fortran namelist-formatted variable + value. + + doctests: + + 1. Check that a true logical variable outputs the correct value: + >>> _get_nml_value_str("banana", "logical", "true") + '.true.' + + 2. Check that a false logical variable outputs the correct value: + >>> _get_nml_value_str("banana", "logical", "0") + '.false.' + + 3. Check that an integer variable outputs the correct value: + >>> _get_nml_value_str("banana", "integer", 5) + '5' + + 4. Check that a real variable outputs the correct value: + >>> _get_nml_value_str("banana", "real", "5d5") + '5d5' + + 5. Check that a real variable with an integer value outputs + the correct value: + >>> _get_nml_value_str("banana", "real", 5) + '5.d0' + + 5. Check that a character variable with no quotes outputs + the correct value: + >>> _get_nml_value_str("banana", "char*10", "apple") + '"apple"' + + 6. Check that a character variable with quotes outputs + the correct value: + >>> _get_nml_value_str("banana", "char*250", " 'apple' ") + "'apple'" + + 7. Check that a character variable with double quotes + outputs the correct value: + >>> _get_nml_value_str("banana", "char*N", ' "apple" ') + '"apple"' + + 8. Check that a character variable with a quotation mark + innternal to the string outputs the correct value: + >>> _get_nml_value_str("banana", "char*31", ''' "app'le" ''') + '"app\\'le"' + + 9. Check that a variable with an unknown type returns + the proper error: + >>> _get_nml_value_str("banana", "apple", "true") # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ocn_in_paramgen.OcnInParamGenError: Namelist type 'apple' for entry 'banana' is un-recognized. + Acceptable namelist types are: logical, integer, real, or char*N. + """ + + #Create set for variable types + #that don't need special handling: + num_set = {"integer", "real"} + + #Check variable type: + if var_type == 'logical': + #If logical, then see if it is "True": + if _is_nml_logical_true(var_name, var_val): + return ".true." + #End if + #If not true, then must be false: + return ".false." + #End if + + if var_type in num_set: + #Check if the variable value is an integer, but is being + #used for a real-type variable: + if var_type == "real" and isinstance(var_val, int): + return f"{var_val}.d0" + #End if + + #Otherwise, simply write value as-is: + return f"{var_val}" + #End if + + if "char" in var_type: + #Remove extra white space: + var_val_strip = var_val.strip() + if '"' in var_val_strip: + var_val_strip = var_val_strip.replace('"','') + var_val_strip = "'" + var_val_strip + "'" + + #Check if string is wrapped in quotes: + quoted_flag = _check_string_quotes(var_name, var_val_strip) + + #If not, then pass string with quotes: + if not quoted_flag: + return f'\'{var_val_strip}\'' + #End if + + #If so, then pass out original string as-is: + return var_val_strip + #End if + + #If one makes it here, then this is an un-recognized type option, so raise an error: + emsg = f"Namelist type '{var_type}' for entry '{var_name}' is un-recognized.\n" + emsg += "Acceptable namelist types are: logical, integer, real, or char*N." + raise OcnInParamGenError(emsg) + +################################################################ +# MAIN "ocn_in" ParamGen class +################################################################ + +class OcnInParamGen(ParamGen): + """ + Encapsulates data and read/write methods for + the ocn_in Fortran namelist file and ParamGen + object. + """ + + def __init__(self, pg_data_dict): + + """ + Initialize a ParamGen object directly + using a ParamGen data dictionary, and + create a new dictionary to match namelist + variables to their associated groups + when reading in "user_nl_blom". + ---------- + pg_data_dict -> python dictionary with ParamGen keys/values + + """ + + #Initialize ParamGen directly: + super().__init__(pg_data_dict) + + #Create a namelist var/group dictionary, + #which is used by the "append_user_nl_file" + #method: + self.__var_group_dict = {} + + #Create empty dictionaries that will contain + #the namelist definition files and the set + #of all namelist groups and variables: + self.__nml_def_groups = {} + self.__nml_def_vars = {} + + #Set variables needed for ParamGen "reduction": + self.__case = None + self.__ocn_attr_dict = None + + #Initialize data structure for duplicate array + #checking in user_nl_blom files. This structure + #is organized like so: + # dict(var_name : list of sets) + # list size = number of array dimensions specified + # set contains the array indices specified for that + # dimension: + self.__set_index_vals = {} + + #### + + @classmethod + def from_namelist_xml(cls, nml_xml_file): + + """ + Initialize ocn_in ParamGen object with XML file, + ---------- + nml_xml_file -> path (str) to namelist definition XML file + + """ + + #Create ParamGen object using base class: + pg_xml = ParamGen.from_xml_nml(nml_xml_file, no_duplicates=True) + + #Initialize new "ocn_in" object: + ocn_in_pg = OcnInParamGen(pg_xml.data) + + #Check if the new ParamGen object has all of the required + #namelist elements: + #---------------- + missing_elems = ocn_in_pg.check_nml_def_elems() + + if missing_elems: + emsg = "The XML namelist definition file:\n" + emsg += f"{nml_xml_file}\n" + emsg += "has namelist entries that are missing required elements.\n" + emsg += "Those entries and missing elements are:\n" + for entry_id, missing_elems in missing_elems.items(): + emsg += f"{entry_id} : {', '.join(missing_elems)}\n" + #End for + raise OcnInParamGenError(emsg) + #End if + #---------------- + + #Initialize file->group/var set dictionary: + ocn_in_pg.__nml_def_groups[nml_xml_file] = set() + ocn_in_pg.__nml_def_vars[nml_xml_file] = set() + + #Create namelist variable/group dictionary + #and associated sets: + #---------------- + for nml_group in ocn_in_pg._data: + for var in ocn_in_pg._data[nml_group]: + + #Check if variable already exists in dictionary: + if var in ocn_in_pg.__var_group_dict: + #No duplicate variables are allowed, even if + #in separate namelist groups, so raise an error. + #Please note that this error should always be + #caught earlier than this, so if it gets to this + #point something has gone seriously wrong: + emsg = f"Namelist entry id '{var}' exists" + emsg += f" in namelist group '{nml_group}'" + emsg += f" and '{ocn_in_pg.__var_group_dict[var]}'\n" + emsg += "Namelist variables can belong to only one group." + raise SystemError(emsg) + #End if + + #If not, then add variable and group to dictionary: + ocn_in_pg.__var_group_dict[var] = nml_group + + #Add namelist groups and variables to their + #respective sets: + ocn_in_pg.__nml_def_groups[nml_xml_file].add(nml_group) + ocn_in_pg.__nml_def_vars[nml_xml_file].add(var) + #End for + #End for + #---------------- + + #Return object: + return ocn_in_pg + + #### + + def check_nml_def_elems(self): + + """ + Function that checks if certain namelist definition + file elements/tags that are optional for ParamGen + but required are present + """ + + #Please note that "group" and "values" are automatically + #required by the ParamGen schema. + + #Required namelist elements: + req_elems = ["type", "desc", "category"] + + #Set missing attributes dictionary: + missing_elems = {} + + #Assume it is a ParamGen object, and loop over namelist groups: + for nml_group in self._data: + #Now loop over variables in group: + for var in self._data[nml_group]: + #Lastly loop over required namelist elements: + for req_elem in req_elems: + #Check if required element is present: + if not req_elem in self._data[nml_group][var]: + #Add missing attribute to dictionary: + if var in missing_elems: + missing_elems[var].append(req_elem) + else: + missing_elems[var] = [req_elem] + #End if + #End if + #End for + #End for + #End for + + #Return missing elements dictionary: + return missing_elems + + #### + + def append_ocn_in_pg(self, ocn_pg_obj): + + """ + Append a new OcnInParamGen object + to this one, ensuring that there are + no duplicate namelist groups or variables. + ---------- + ocn_pg_obj -> An OcnInParamGen object + + """ + #Loop over all XML files associated with input ocn_pg object: + for input_file in ocn_pg_obj.__nml_def_groups: + + #Extract the group and variable sets from input PG object: + input_groups = ocn_pg_obj.__nml_def_groups[input_file] + input_vars = ocn_pg_obj.__nml_def_vars[input_file] + + #Check that there are no matching namelist groups: + #------------------------------------------------ + + #Initialize error message string: + emsg = "" + + #Loop over all namelist files and namelist group sets: + for nml_file, nml_groups in self.__nml_def_groups.items(): + + #Determine if any namelist groups are the same + #between the two objects: + same_groups = nml_groups.intersection(input_groups) + + #If so, then add to error message (as all namelist groups must be unique): + if same_groups: + emsg += f"Cannot append:\n'{input_file}'\n" + emsg += " The following namelist groups conflict with those in" + emsg += f"\n'{nml_file} :'\n" + emsg += ", ".join(same_groups) + #End if + #End for + + #------------------------------------------------ + #Check that there are no matching namelist variables: + #------------------------------------------------ + for nml_file, nml_vars in self.__nml_def_vars.items(): + + #Determine if any namelist groups are the same + #between the two objects: + same_vars = nml_vars.intersection(input_vars) + + #If so, then add to error message (as all namelist variable ids must be unique): + if same_vars: + emsg += f"Cannot append:\n'{input_file}'\n" + emsg += " The following namelist variablesconflict with those in" + emsg += f"\n'{nml_file} :'\n" + emsg += ", ".join(same_vars) + #End if + #End for + #------------------------------------------------ + + #End for (input files used to create input ocn_pb object) + + #Check if an error message was written. If so then raise the + #error(s) here: + if emsg: + raise OcnInParamGenError(emsg) + #Endd if + + #Add input PG object dictionaries to this object's dicts: + self.__nml_def_groups.update(ocn_pg_obj.__nml_def_groups) + self.__nml_def_vars.update(ocn_pg_obj.__nml_def_vars) + + #Also combine PG object var-group dictionary needed for + #appending "user_nl_blom": + self.__var_group_dict.update(ocn_pg_obj.__var_group_dict) + + #Append input PG object to this object: + self.append(ocn_pg_obj) + + #### + + def get_user_nl_var_array_info(self, var_str): + + """ + Checks whether the variable string + is for a specific set of array + indices. + ---------- + var_str -> variable name string. + + outputs: + ---------- + is_array -> Logical for whether variable + is an array. + var_name -> Name of variable + (with array indices stripped). + arr_indxs -> List of lists, with one list + for each array dimension. Each + dimension list contains all duplicated + indices for that dimension. + data_group -> Namelist group for that particular + variable. + + """ + + #Initialize variable name + var_name = var_str + + #Initialize array index list: + arr_indxs = [] + + #Check for array syntax, i.e. parentheses: + array_syntax_match = _ARR_INDEX_REGEX.search(var_str) + + #Extract variable name: + if array_syntax_match: + var_name = var_str[:array_syntax_match.start(0)] + else: + var_name = var_str + #End if + + #Check that variable actually exists in ParamGen object: + + if var_name in self.__var_group_dict: + #Extract namelist group list for variable: + data_group = self.__var_group_dict[var_name] + + else: + #Raise error that namelist variable isn't listed in + #anywhere in a definition file: + emsg = f"Variable '{var_name}' not found in any namelist definition files." + emsg += " Please double-check 'user_nl_blom'." + raise OcnInParamGenError(emsg) + #End if + + #Extract variable type from ParamGen Object: + var_type = self._data[data_group][var_name]["type"] + + #Search for array dimension specifications in type: + array_type_dims = _ARRAY_TYPE_REGEX.search(var_type) + + #Determine if variable is actually an array or not: + is_array = bool(array_type_dims) + + #Exit function here if no array indices were used in user_nl_blom file: + if not array_syntax_match: + + #No parantheses used, so no indices need to be checked: + return is_array, var_name, arr_indxs, data_group + #End if + + #If variable is not an array, but array indices are being + #used in user_nl_blom, then throw an error: + if not is_array: + emsg = f"Variable '{var_name}' is not an array, but array" + emsg += " dimensions are being specified in 'user_nl_blom'." + raise OcnInParamGenError(emsg) + #End if + + #Extract array dimension information from variable type + #as listed in the associated namelist definition file: + #---------------------------------------------------- + + #Pull out dimensions string: + array_dim_text = array_type_dims.group(1) + + #Split text by number of commas (which should indicate dimensions): + array_dims_list = array_dim_text.split(",") + + #Extract total number of dimensions: + num_arr_dims = len(array_dims_list) + + #Create new list of max dim size: + max_dim_sizes = [] + for dim_size in array_dims_list: + max_dim_sizes.append(user_nl_str_to_int(dim_size, var_name)) + #End for + + #---------------------------------------------------- + + #Now extract all text inside variable quotes: + user_array_text = array_syntax_match.group(1) + + #Split text by number of commas (which should indicate dimensions): + user_dim_text = user_array_text.split(",") + + #Check that the user hasn't listed the wrong number of dimensions: + num_user_dims = len(user_dim_text) + if num_user_dims != num_arr_dims: + #Set proper grammar: + if num_user_dims == 1: + user_dim_str = "dimension" + else: + user_dim_str = "dimensions" + #End if + if num_arr_dims == 1: + array_dim_str = "dimension." + else: + array_dim_str = "dimensions." + #End if + + #Raise error with proper message: + emsg = f"Variable '{var_name}' has {num_user_dims}" + emsg += f" {user_dim_str} used in 'user_nl_blom', but is defined" + emsg += f" to have {num_arr_dims} {array_dim_str}" + raise OcnInParamGenError(emsg) + #End if + + #Loop over dimensions: + for dim_idx, array_index_text in enumerate(user_dim_text): + #Create new array list entry: + if array_index_text.strip() == ':': + #Only a single colon provided. In this case provide a special index + #that indicates that specific indices can still be provided, but that the + #whole array dimension cannot be written again: + arr_indxs.append([-1]) + else: + indices = parse_dim_spec(var_name, array_index_text, + max_dim_sizes[dim_idx]) + if not indices: + ## Log a warning here if no values were returned? + pass + #End if + arr_indxs.append(indices) + #End if + #End for (dimensions) + + #Return relevant variables: + return is_array, var_name, arr_indxs, data_group + + #### + + def check_array_indices(self, var_name, arr_index_list): + + """ + Checks whether the list of array indices has already + been set for the given variable, and if so, raises + an error. + ---------- + var_name -> Name of array variable being modified. + + arr_index_list -> A list of lists of array indices + with the first list representing + the dimensions, and the second list + containing the array indices being + set for that dimension. + + """ + + #Initialize duplicated index flag, + #We won't know the answer one way or the other + #until either the end of the loop below, or + #until certain conditions are met, so for now + #initialize as "None": + is_arr_dupl = None + + #Initialize "possible" array duplication flag: + possible_dupl = False + + #Check if variable name exists in dictionary: + if not var_name in self.__set_index_vals: + #Create a new entry with an empty list, + #it should then be filled out in the loop below: + self.__set_index_vals[var_name] = [] + + #Also set duplication to "False": + is_arr_dupl = False + #End if + + #Initialize dimension index for use in error-handling at + #end of function: + dim_indx = 0 + + #Loop over each separate dimension list: + for dim_indx, dim_arr_indxs in enumerate(arr_index_list): + + #Initialize duplicated index list (for last dimension checked): + dup_indx_list = [] + + #Check if dimension index exists for variable dictionary: + if dim_indx == len(self.__set_index_vals[var_name]): + #Create a new set of array indices for the new dimensions: + self.__set_index_vals[var_name].append(set(dim_arr_indxs)) + + #Since a new dimension is being specified, this is not a duplicate: + is_arr_dupl = False + else: + #Loop over all array indices: + for arr_indx in dim_arr_indxs: + #Check if array index has already been explicitly called: + if arr_indx in self.__set_index_vals[var_name][dim_indx]: + #Add array index to list of duplicated values: + dup_indx_list.append(arr_indx) + + #This line is possibly a duplication, + #but will need to finish the loop to be sure: + possible_dupl = True + #End if + + #Add index to "set index" set for variable: + self.__set_index_vals[var_name][dim_indx].add(arr_indx) + #End for (array indices) + + #If there were no duplicates at this dimension, then this entry + #is not a duplicate: + if not possible_dupl: + is_arr_dupl = False + #End if + + #End if (new dimension) + #End for (dimensions) + + #If the duplication flag hasn't been set yet, then set it now: + if is_arr_dupl is None: + is_arr_dupl = possible_dupl + #End if + + #Now raise an error if there is array duplication: + if is_arr_dupl: + if any(dup == -1 for dup in dup_indx_list): + #This is a special case where a non-bounded + #colon (:) was repeated twice, so write + #the error message accordingly: + emsg = f"Variable '{var_name}' has all values" + emsg += " being set multiple times for" + emsg += f" dimension {dim_indx+1}." + else: + emsg = f"Variable '{var_name}' has values" + emsg += " at the following indices being" + emsg += " set multiple times for dimension" + emsg += f" ({dim_indx+1}) :\n" + emsg += ", ".join(str(dup) for dup in dup_indx_list) + #End if + raise OcnInParamGenError(emsg) + #End if + + #### + + def append_user_nl_file(self, user_nl_file): + """ + Reads in user_nl_blom files and converts + them to the proper ParamGen syntax. + ---------- + user_nl_file -> Path (str) to user_nl_blom file. + + """ + + #Create ordered dictionary to store namelist groups, + #variables, and values from user_nl_blom file: + _data = OrderedDict() + + #Initialize flag preventing duplicate namelist entries: + no_duplicates = True + + #Initialize flag to mark whether a variable is an array: + is_array = False + + #Initialize flag to mark whether the line is an array continuation line: + is_continue_line = False + + #Open user_nl_blom file: + with open(user_nl_file,'r', encoding='utf-8') as user_file: + for line_num, line in enumerate(user_file): + if len(line)>1: + #Split line into a list of words/characters: + line_s = line.split() + + #If line is empty then go to next line: + if not line_s: + continue + #End if + + #Check if a comment delimiter is somewhere in the string: + if '!' in line: + #Check if the entire line is a comment: + if line_s[0][0] == "!": + #Check if this comment is the duplicate keyword: + if "allow_duplicate_namelist_entries" in line_s: + #Next check if a user has set variable to True: + for word in line_s: + if word.lower() == "true": + #Allow duplicate namelist entries: + no_duplicates = False + break + #End if + #End for + #End if + #Continue to next line in file: + continue + #End if + #Otherwise simply remove any part of the line that is commented out: + line = remove_user_nl_comment(line) + #End if + + #Check if the first character on the line is a comma (,): + if line.strip()[0] == ",": + #Is this an array variable: + if is_array: + #Was a continuation line already provided: + if is_continue_line: + #Two commas were used in a row with a newline + #in-between. Technically this is allowed, + #but in practice it is VERY likely a mistake, + #so raise an error here: + emsg = f"Line number {line_num+1} in 'user_nl_blom'" + emsg += " starts with a comma (,) but the" + emsg += " previous line ended with a comma." + emsg += "\nPlease remove one of the commas." + raise OcnInParamGenError(emsg) + #End if + + #If not, then set it to be a continuation line: + is_continue_line = True + else: + #The previous variable is not an array, so throw an error: + emsg = f"Line number {line_num+1} in 'user_nl_blom'" + emsg += " starts with a comma (,) but the" + emsg += " associated namelist variable is not an array." + raise OcnInParamGenError(emsg) + #End if + #End if + + #Now parse the line: + if "=" in line and (line.strip()[0] != "=") and not (is_array and is_continue_line): + line_ss = line.split("=") # Split line into before/after equals sign + var_str = (line_ss[0]).strip() # the first element is the variable name + var_str = var_str.lower() + + #Check if this variable is an array, and if so, + #then return what the variable name is, what indices (if any) + #are being specified, and what namelist (data) group it belongs to: + is_array, var_name, arr_indxs, data_group = \ + self.get_user_nl_var_array_info(var_str) + + #Check if variable can actually be set in user_nl_blom - or must be specified + #only via xmlchange + user_only_modify_via_xml = self._data[data_group][var_str]["modify_via_xml"] != 'unset' + if user_only_modify_via_xml: + xml_var = self._data[data_group][var_str]["modify_via_xml"] + emsg = f"Cannot change {var_str} in user_nl_blom file," + emsg += f" can only set {var_str} via xmlchange to {xml_var.upper()} \n" + raise OcnInParamGenError(emsg) + + #Are there array indices specified: + if arr_indxs: + + if no_duplicates: + #Check if any duplicate array indices are present: + self.check_array_indices(var_name, arr_indxs) + #End if + + #ParamGen will think this is a "new" parameter variable, so we need + #to add a type in order for the "write" method to work properly. The + #type can be copied directly from the original variable using "var_name": + var_type = self._data[data_group][var_name]["type"] + else: + #Variable doesn't need a type specified: + var_type = None + #End if (array indices) + + #Extract value string: + val_str = ' '.join(line_ss[1:]) # the rest is the value string + + #Check if value string ends in array continuation: + if is_array: + #Check if the string ends in a comma (,): + if val_str.strip()[-1] == ",": + #If so, then make "array_continue_line" fully true: + is_continue_line = True + #End if + #End if + + #Add the namelist group if not already in data dict: + if not data_group in _data: + _data[data_group] = {} + #End if + + #Check if variable already exists in data dictionary: + if var_str in _data[data_group] and no_duplicates: + emsg = "Namelist variable '{}' set more than once in '{}'" + emsg += "\nPlease set each variable only once." + raise OcnInParamGenError(emsg.format(var_str, user_nl_file)) + #End if + + #Enter the parameter in the dictionary: + if var_type: + _data[data_group][var_str] = {'values':val_str, 'type':var_type} + else: + _data[data_group][var_str] = {'values':val_str} + #end if + elif (is_array and is_continue_line): + #See if there is an equals sign outside of quotes by treating it like + #a comment delimiter, and seeing if characters in the string are removed: + no_equals_line = remove_user_nl_comment(line, comment_delim='=') + if len(no_equals_line) != len(line): + #This looks like the start of a new namelist entry without the + #proper ending of the previous entry. So raise an error here: + emsg = f"Line number {line_num+1} in 'user_nl_blom' appears" + emsg += " to be starting a new namelist entry,\nbut" + emsg += " the previous entry has a trailing comma (,). Please fix." + raise OcnInParamGenError(emsg) + #End if + + #This is an array continuation line, so append the line to previous + #variable's value as-is: + _data[data_group][var_str]['values'] += line + + #Check if the line does NOT end in a comma (,): + if not line.strip()[-1] == ",": + #Notify loop to check the next line for a comma: + is_continue_line = False + #End if + + else: + emsg = "Cannot parse the following line in '{}' :\n'{}'" + raise OcnInParamGenError(emsg.format(user_nl_file, line)) + #End if ("=" sign check) + #End if (len(line) > 1) + #End for + #End with + + #Check if any user_nl_blom data is present: + if _data: + #If so, then create new ParamGen object: + pg_user = ParamGen(_data) + + #Append new user_nl_blom object to main ocn_in namelist object: + self.append(pg_user) + #End if + #### + + def set_value(self, variable, value): + """ + Reset value of namelist variable + ---------- + """ + + #Loop through namelist groups in alphabetical order: + for nml_group in sorted(self._data): + #Create function to properly sort variables with array indices: + var_sort_key = lambda var : var[:var.index("(")] if "(" in var else var + # Change value of variable + for var in sorted(self._data[nml_group], key=var_sort_key): + if var == variable: + self._data[nml_group][var]["values"] = value + break + + def get_value(self, variable): + """ + Get value of namelist variable + ---------- + """ + + #Loop through namelist groups in alphabetical order: + for nml_group in sorted(self._data): + #Create function to properly sort variables with array indices: + var_sort_key = lambda var : var[:var.index("(")] if "(" in var else var + # Change value of variable + for var in sorted(self._data[nml_group], key=var_sort_key): + if var == variable: + value = self._data[nml_group][var]["values"] + break + return value + + def write_nmlfile(self, output_path, groups, empty_namelists=None): + """ + Write data to Fortran namelist file. + ---------- + output_path -> path (str) to Fortran namelist (ocn_in) file + """ + + # Make sure ParamGen object has been reduced: + if not self.reduced: + emsg = "ParamGen object for ocn_in must be reduced before being " + emsg += "written to file. Please check BLOM's buildnml script." + raise OcnInParamGenError(emsg) + #End if + + #Create function to properly sort variables with array indices: + var_sort_key = lambda var : var[:var.index("(")] if "(" in var else var + + # Write Fortran namelist file: + with open(os.path.join(output_path), 'w', encoding='utf-8') as ocn_in_fil: + + #Loop through namelist groups in alphabetical order: + for nml_group in sorted(self._data): + if nml_group in groups: + # Write namelist group: + ocn_in_fil.write("&"+nml_group+"\n") + + # Write all variables within that group (sorted alphabetically): + for var in sorted(self._data[nml_group], key=var_sort_key): + + #Extract variable value(s): + val = self._data[nml_group][var]["values"].strip() + + #If no value is set then move to the next variable: + if val is None: + continue + #End if + + #Extract variable type: + if "type" in self._data[nml_group][var]: + var_type = self._data[nml_group][var]["type"].strip() + else: + emsg = f"Namelist entry '{var}' is missing required 'type' element." + raise OcnInParamGenError(emsg) + #End if + + # @ is used in a namelist to put the same namelist variable in multiple groups + # in the write phase, all characters in the namelist variable name after + # the @ and including the @ should be removed + if "@" in var: + var = re.sub("@.+$", "", var) + + #Check if an array type: + array_type = _ARRAY_TYPE_REGEX.search(var_type) + + if array_type: + #Grab all text before array regex match: + var_type = var_type[:array_type.start()].strip() + + #Split the value into its array elements, + #this assumes that a comma (,) is the element + #delimiter: + array_elems = val.split(",") + + #Write beginning of namelist entry: + nml_str = f" {var} = " + + #loop over array elements: + for elem in array_elems: + + #Get properly-formatted variable value: + nml_val = _get_nml_value_str(var, var_type, elem) + + #Add value string (with comma) to namelist string: + nml_str += (nml_val + ", ") + #End for + + #There will always be a trailing comma and space (, ) so find it: + last_comma_idx = nml_str.rfind(", ") + + #Write final string to file: + ocn_in_fil.write(nml_str[:last_comma_idx]+"\n") + + else: #Not an array + + #Get properly-formatted variable value: + nml_val = _get_nml_value_str(var, var_type, val) + + #Write variable to namelist file: + ocn_in_fil.write(f' {var} = {nml_val}\n') + + #End if (array type) + #End for (namelist variables) + # Add space for next namelist group: + ocn_in_fil.write('/\n\n') + + #End for (namelist groups) + + # Write out empty namelists if needed + if empty_namelists is not None: + for empty_namelist in empty_namelists: + ocn_in_fil.write("&"+empty_namelist+"\n") + ocn_in_fil.write('/\n\n') + + #End with (open ocn_in file) + + #### + + def write_inputdata(self, output_path, groups): + + """ + Write data to Fortran namelist file. + ---------- + output_path -> path (str) to blom input data list file + + """ + + # Make sure ParamGen object has been reduced: + if not self.reduced: + emsg = "ParamGen object for ocn_in must be reduced before being " + emsg += "written to file. Please check BLOM's buildnml script." + raise OcnInParamGenError(emsg) + #End if + + #Create function to properly sort variables with array indices: + var_sort_key = lambda var : var[:var.index("(")] if "(" in var else var + + # Write Fortran namelist file: + with open(os.path.join(output_path), 'w', encoding='utf-8') as ocn_in_fil: + + #Loop through namelist groups in alphabetical order: + for nml_group in sorted(self._data): + if nml_group in groups: + + # Write all variables within that group (sorted alphabetically): + for var in sorted(self._data[nml_group], key=var_sort_key): + + if self._data[nml_group][var]["is_inputdata"] == 'yes': + val = self._data[nml_group][var]["values"].strip() + val = val.replace('"','') + write_val = True + if val == "''": + write_val = False + elif 'unset' in val or 'UNSET' in val: + write_val = False + if write_val: + ocn_in_fil.write(f"{var} = {val} \n") + + + def reduce_ocn_in(self, case, ocn_attr_dict): + """ + Reduce XML namelist attributes + (i.e. replace attribute/guard dictionary with value) + ---------- + case -> dictionary replicating a case object + ocn_attr_dict -> dictionary containing attribute values + + """ + + # Set internal variables for use by "expand_func": + self.__case = case + self.__ocn_attr_dict = ocn_attr_dict + + # Reduce Param Data: + self.reduce(self.__expand_func) + + #### + + def __expand_func(self, varname): + + """ + Function used to convert $XXX + variables and XML attributes to + their associated values. + """ + + #Check if varname matches a case dictionary variable: + val = self.__case.get_value(varname) + + #If not, then attempt to extract variable from + #attribute dictionary: + if val is None: + if varname in self.__ocn_attr_dict: + val = self.__ocn_attr_dict[varname] + else: + #Assume the XML attribute/guard is an empty string: + val = "" + #End if + #End if + + #Return value if found: + return val + +############ +#End of file diff --git a/cime_config/testdefs/testlist_blom.xml b/cime_config/testdefs/testlist_blom.xml index f54c99eb..83842ba0 100644 --- a/cime_config/testdefs/testlist_blom.xml +++ b/cime_config/testdefs/testlist_blom.xml @@ -5,22 +5,22 @@ - + - + - + - + diff --git a/cime_config/user_nl_blom b/cime_config/user_nl_blom index bdfa712d..00abb174 100644 --- a/cime_config/user_nl_blom +++ b/cime_config/user_nl_blom @@ -1,7 +1,34 @@ -#---------------------------------------------------------------------------------- -# Users should add all user specific namelist changes below in the form of -# namelist_var = new_namelist_value -# Note - that it does not matter what namelist group the namelist_var belongs to -#---------------------------------------------------------------------------------- +!---------------------------------------------------------------------------------- +! Users should add all user specific namelist changes below in the form of +! namelist_var = new_namelist_value +! +! NOTE: that it does not matter what namelist group the namelist_var belongs to +! EXCEPT for when a variable appears in more than one namelist +! There are a few history variables that have the same name in both diaphy and diabgc +! For those variables an @diaphy or @diabgc to be appendend for the namelist_var +! +! GLB_FNAMETAG@diaphy +! GLB_AVEPERIO@diaphy +! GLB_FILEFREQ@diaphy +! GLB_COMPFLAG@diaphy +! GLB_NCFORMAT@diaphy +! LYR_DP@diaphy"> +! GLB_FNAMETAG@diabgc +! GLB_AVEPERIO@diabgc +! GLB_FILEFREQ@diabgc +! GLB_COMPFLAG@diabgc +! GLB_NCFORMAT@diabgc +! LYR_DP@diabgc"> +! +! For example: +! GLB_FNAMETAG@diaphy = 'hd','hd','hd' +! +! NOTE: the array sections for MER_REGFLG must be entered as +! MER_REGFLG1 - sets MER_REGFLG(1,:) +! MER_REGFLG2 - sets MER_REGFLG(2,:) +! MER_REGFLG3 - sets MER_REGFLG(3,:) +! MER_REGFLG4 - sets MER_REGFLG(4,:) +! To see a full documentation of the namelist variables - see $RUNDIR/ocn_in.readme +!----------------------------------------------------------------------------------