AUSTAL (modified)
TalStt.c
/*===================================================================== TalStt.c
*
* Read Settings for ARTM, re-imported to AUSTAL
* =============================================
*
* Copyright (C) Umweltbundesamt, Dessau-Roßlau, Germany, 2002-2024
* Copyright (C) Janicke Consulting, 88662 Überlingen, Germany, 2002-2024
* Email: info@austal.de
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* last change: 2024-01-17 uj
*
*============================================================================*/
char *TalSttVersion = "3.3.0";
static char *eMODn = "TalStt";
static int CHECK = 0;
#include
#include
#include
#include
#include
#include
#include "IBJmsg.h"
#include "IBJary.h"
#include "IBJtxt.h"
#include "IBJdmn.h"
#include "TalStt.h"
typedef struct {
char *_name; // parameter name + buffer for tokens
int nTokens; // number of tokens
char **_tokens; // array of pointers to tokens
double *_dd; // array of values or NULL
} INPREC;
char **SttCmpNames; // names of emittable components
STTSPCREC *SttSpcTab; // species defined
STTSPCREC SttAstlDefault;
STTSPCREC SttArtmDefault;
int SttSpcCount, SttCmpCount, SttTypeCount, SttMode;
char *SttGroups[STTGRPSIZE] = { "gas", "gas", "gas", "pm3", "pm4", "pmu" };
char *SttGrpXten[STTGRPSIZE] = { "", "-1", "-2", "-3", "-4", "-u" };
double SttVdVec[STTGRPSIZE] = { 0, 0.001, 0.01, 0.05, 0.20, 0.07 }; //-2005-09-12
double SttVsVec[STTGRPSIZE] = { 0, 0.000, 0.00, 0.04, 0.15, 0.06 }; //-2005-09-12
double SttWfVec[STTGRPSIZE] = { 0, 1.0e-5, 2.0e-5, 3.0e-5, 4.0e-5, 5.0e-5 }; //-2005-09-12
double SttWeVec[STTGRPSIZE] = { 0, 0.6, 0.6, 0.6, 0.6, 0.6 }; //-2005-04-12
//char SttRiSep[256] = "NIEDERSCHLAG:"; //-2011-12-06 -2024-01-17
double SttRiVec[1] = { 0 }; // deprecated, AKS with rain -2024-01-17
int SttNstat = 1;
double SttNoxTimes[6] = { 2.9, 2.5, 1.9, 1.3, 0.9, 0.3 };
double SttHmMean[6] = { 0, 0, 0, 800, 1100, 1100 };
double SttOdorThreshold = 0.25;
double SttSrcTurbulence = 0.1;
double SttGmmB1 = 1.00;
double SttGmmB2 = 0.14286;
char SttGmmUnit[16] = "Bq/m²";
double SttGmmFd = 4;
double SttGmmFe = 3;
double SttGmmFf = 10;
double SttGmmFr = 10;
double SttGmmMue = 0.0082;
double SttGmmRef = 1.e-6;
double SttGmmMu[2] = { 7.78e-3, 1.82e-2 };
double SttGmmDa[2][6] = {
{ 1.0, 0.77, 0.35, -4.00e-2, 3.20e-3, -8.20e-5 },
{ 1.0, 1.92, 1.74, -3.39e-2, 3.86e-2, -2.11e-3 }
};
double SttGmmBk[2][4][4] = {
{ { 0.485, 0.064, 1.705, -1.179 },
{ 0.137, 1.878, -4.817, 2.883 },
{ -0.0035, -0.8569, 2.0527, -1.2552 },
{ -0.0018, 0.0997, -0.2392, 0.1503 } },
{ { 0.279, 0.595, -0.205, 0.622 },
{ 0.135, 0.866, -0.716, -0.578 },
{ -0.0131, -0.324, 0.1103, 0.2892 },
{ 0.0003, 0.0313, -0.0017, -0.0337 } }
};
static char cbreak = '#';
static int scanning = 0;
#define buflen 256
static void trim(char *buf) {
int n, i, quote=0;
if (!buf) return;
n = strlen(buf);
for (i=0; i if (buf[i] == '"') {
if (i > 0 && buf[i-1] == '\\') continue;
quote = (quote == 0);
continue;
}
if (quote) continue;
if (buf[i] == cbreak) {
buf[i] = 0;
n = i;
break;
}
}
for (i=n-1; i>=0; i--) {
if (buf[i] <= ' ') buf[i] = 0;
else break;
}
}
static int starts_with(char *source, char *s) {
int rc;
if (!source || !s) return 0;
rc = (0 == strncmp(source, s, strlen(s)));
return rc;
}
static int ends_with(char *source, char *s) {
int rc, l;
if (!source || !s) return 0;
l = strlen(source) - strlen(s);
if (l < 0) return 0;
rc = (0 == strcmp(source+l, s));
return rc;
}
//================================================================= parse
static int parse( char *line, INPREC *pp ) {
dP(parse);
int i, n, l;
int is_number = 1;
char *p1, c, tk[256];
if (!pp) eX(2);
pp->_name = NULL;
pp->nTokens = 0;
pp->_tokens = NULL;
pp->_dd = NULL;
if (line==NULL || !*line)
return 0;
l = strlen(line);
pp->_name = ALLOC(l+1); if (!pp->_name) eX(1);
strcpy(pp->_name, line);
for (p1=pp->_name; isalnum(*p1) || strchr("()", *p1); p1++);
if (!*p1)
return 0;
*p1++ = 0;
if (!*pp->_name)
return 0;
pp->_tokens = _MsgTokens(p1, " ;\t\r\n=");
if (!pp->_tokens)
return 0;
for (n=0; ; n++)
if (!pp->_tokens[n])
break;
if (n == 0)
return 0;
pp->nTokens = n;
for (i=0; i c = *pp->_tokens[i];
if (!isdigit(c) && c!='-' && c!='+')
is_number = 0;
}
if (is_number) {
char *tail;
pp->_dd = ALLOC(n*sizeof(double));
for (i=0; i strncpy(tk, pp->_tokens[i], 255); //-2003-07-07
tk[255] = 0;
for (p1=tk; (*p1); p1++) if (*p1 == ',') *p1 = '.'; //-2003-07-07
pp->_dd[i] = strtod(tk, &tail);
if (*tail) {
FREE(pp->_dd);
pp->_dd = NULL;
is_number = 0;
break;
}
}
}
if (!is_number) {
for (i=0; i MsgUnquote(pp->_tokens[i]); //-2001-09-04
}
return is_number;
eX_1: eX_2:
eMSG("Internal error!");
}
static char *set_string(char *s, INPREC inp) {
static char msg[256];
if (CHECK) vMsg("setting string %s", inp._name);
if (inp.nTokens < 1) {
sprintf(msg, "no string data for %s", inp._name);
return msg;
}
if (!inp._tokens) {
sprintf(msg, "missing string for %s", inp._name);
return msg;
}
if (strlen(inp._tokens[0]) > 15) {
sprintf(msg, "string too long for %s", inp._name);
return msg;
}
strcpy(s, inp._tokens[0]);
return NULL;
}
static char *set_float(float *pf, INPREC inp) {
static char msg[256];
if (CHECK) vMsg("setting float %s", inp._name);
if (inp.nTokens < 1) {
sprintf(msg, "no float data for %s", inp._name);
return msg;
}
if (!inp._dd) {
sprintf(msg, "missing float for %s", inp._name);
return msg;
}
*pf = (float)inp._dd[0];
return NULL;
}
static char *set_doubles(double *pf, INPREC inp, int n) {
static char msg[256];
int i;
if (CHECK) vMsg("setting doubles %s", inp._name);
if (!inp._dd) {
sprintf(msg, "missing doubles for %s", inp._name);
return msg;
}
if (n > inp.nTokens) n = inp.nTokens;
for (i=0; i pf[i] = inp._dd[i];
return NULL;
}
static char *set_integer(int *pi, INPREC inp) {
static char msg[256];
if (CHECK) vMsg("setting integer %s", inp._name);
if (inp.nTokens < 1) {
sprintf(msg, "no integer data for %s", inp._name);
return msg;
}
if (!inp._dd) {
sprintf(msg, "missing integer for %s", inp._name);
return msg;
}
*pi = (int)inp._dd[0];
return NULL;
}
#define TRY_STRING(a) if (!strcmp(inp._name, #a)) msg = set_string(pr->a, inp)
#define TRY_FLOAT(a) if (!strcmp(inp._name, #a)) msg = set_float(&(pr->a), inp)
#define TRY_INTEGER(a) if (!strcmp(inp._name, #a)) msg = set_integer(&(pr->a), inp)
#define TRY_DOUBLES(a,b,c) if (!strcmp(inp._name, #a)) msg = set_doubles((b), inp, (c))
#define TRY_TEXT(a,b) if (!strcmp(inp._name, #a)) msg = set_string((b), inp)
static char *analyse_spec(INPREC inp, STTSPCREC *pr) {
char *msg = NULL;
if (CHECK) vMsg("analyse spec: name=%s, section=%s", inp._name, pr->name);
TRY_STRING(grps);
else TRY_STRING(cadd); //-2024-01-17
else TRY_STRING(unit);
else TRY_FLOAT(vd);
else TRY_FLOAT(wf);
else TRY_FLOAT(we);
else TRY_FLOAT(de);
else TRY_FLOAT(fr);
else TRY_FLOAT(fc);
else TRY_STRING(uc);
else TRY_FLOAT(fn);
else TRY_STRING(un);
else TRY_FLOAT(ry);
else TRY_INTEGER(dy);
else TRY_INTEGER(nd);
else TRY_FLOAT(rd);
else TRY_INTEGER(dd);
else TRY_INTEGER(nh);
else TRY_FLOAT(rh);
else TRY_INTEGER(dh);
else TRY_FLOAT(rn);
else TRY_INTEGER(dn);
else msg = "unknown parameter";
return msg;
}
static char *analyse_astl(INPREC inp) {
char *msg = NULL;
TRY_DOUBLES(odorthreshold, &SttOdorThreshold, 1);
else TRY_DOUBLES(srcturbulence, &SttSrcTurbulence, 1);
else TRY_DOUBLES(noxtimes, SttNoxTimes, 6);
else msg = "unknown astl-parameter";
return msg;
}
static char *analyse_artm(INPREC inp) {
char *msg = NULL;
TRY_TEXT(gmunit, SttGmmUnit);
else TRY_DOUBLES(gmref, &SttGmmRef, 1);
else TRY_DOUBLES(gmfd, &SttGmmFd, 1);
else TRY_DOUBLES(gmfe, &SttGmmFe, 1);
else TRY_DOUBLES(gmff, &SttGmmFf, 1);
else TRY_DOUBLES(gmfr, &SttGmmFr, 1);
else TRY_DOUBLES(gmmu10, &SttGmmMu[0], 1);
else TRY_DOUBLES(gmmu01, &SttGmmMu[1], 1);
else TRY_DOUBLES(gmda10, SttGmmDa[0], 6);
else TRY_DOUBLES(gmda01, SttGmmDa[1], 6);
else TRY_DOUBLES(gmbk10(0), SttGmmBk[0][0], 4);
else TRY_DOUBLES(gmbk10(1), SttGmmBk[0][1], 4);
else TRY_DOUBLES(gmbk10(2), SttGmmBk[0][2], 4);
else TRY_DOUBLES(gmbk10(3), SttGmmBk[0][3], 4);
else TRY_DOUBLES(gmbk01(0), SttGmmBk[1][0], 4);
else TRY_DOUBLES(gmbk01(1), SttGmmBk[1][1], 4);
else TRY_DOUBLES(gmbk01(2), SttGmmBk[1][2], 4);
else TRY_DOUBLES(gmbk01(3), SttGmmBk[1][3], 4);
else msg = "unknown artm-parameter";
return msg;
}
static char *analyse_system(INPREC inp) {
char *msg = NULL;
TRY_DOUBLES(vdvec, SttVdVec+1, 5);
else TRY_DOUBLES(vsvec, SttVsVec+1, 5);
else TRY_DOUBLES(wfvec, SttWfVec+1, 5);
else TRY_DOUBLES(wevec, SttWeVec+1, 5);
//else TRY_DOUBLES(rivec, SttRiVec, 6); //-2011-12-06 -2024-01-17
else TRY_DOUBLES(hmmean, SttHmMean, 6);
//else if (!strcmp(inp._name, "risep")) //-2011-12-06 -2024-01-17
// msg = set_string(SttRiSep, inp);
else msg = "unknown system-parameter";
return msg;
}
#undef TRY_STRING
#undef TRY_FLOAT
#undef TRY_DOUBLES
#undef TRY_INTEGER
#undef TRY_TEXT
static int read_section(FILE *f, char *buf, int nn, STTSPCREC *pr) {
dP(read_section);
int n=0, l, i;
char *pc, *msg, name[16];
INPREC inp;
if (f==NULL || buf==NULL) eX(1);
l = strlen(buf);
if (l < 2 || l > 17) eX(2);
strncpy(name, buf+1, l-2);
MsgLow(name);
name[l-2] = 0;
if (pr) {
if (SttMode) {
memcpy(pr, &SttArtmDefault, sizeof(STTSPCREC));
}
else {
memcpy(pr, &SttAstlDefault, sizeof(STTSPCREC));
}
strcpy(pr->name, name);
}
// pr->wf = -1;
// pr->wc = -1;
while(1) {
*buf = 0;
pc = fgets(buf, buflen, f);
if (pc == NULL)
break;
n++;
if (strlen(buf) >= buflen-2) eX(3);
trim(buf);
if (!*buf)
continue;
if (*buf == '[')
break;
if (0 > parse(buf, &inp)) eX(4);
if (CHECK) vMsg("> %s", buf);
MsgLow(inp._name);
if (pr == NULL) { //-2005-10-26
msg = analyse_system(inp);
}
else {
msg = analyse_spec(inp, pr);
if (msg && starts_with(msg, "unknown parameter")) {
if (!strcmp(pr->name, ".astl"))
msg = analyse_astl(inp);
else if (!strcmp(pr->name, ".artm"))
msg = analyse_artm(inp);
}
}
FREE(inp._name);
FREE(inp._tokens);
if (inp._dd != NULL) {
FREE(inp._dd);
inp._dd = NULL;
}
if (msg && *msg) {
vMsg("error in line %d: %s\n%s", n+nn, buf, msg);
scanning = 1;
}
}
//
if (pr) {
if (!strncmp(pr->name, "no", 2)) pr->de = 0; //-2005-09-01
if (SttMode) {
if (strcmp(pr->unit, "Bq")) eX(5);
}
else {
if (!strcmp(pr->unit, "Bq")) eX(6);
}
//
// basic unit checks //-2022-01-31
if (!strcmp(pr->unit, "OU")) {
double ff = -1.;
if (!strcmp(pr->uc, "%"))
ff = 100.;
else
vMsg("%s: unexpected output unit %s, conversion factor fc not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fc - ff)/ff > 0.0001) eX(8);
}
}
else if (!strcmp(pr->unit, "1")) {
double ff = -1.;
if (!strcmp(pr->uc, "1/m3"))
ff = 1.;
else if (!strcmp(pr->uc, "1/cm3"))
ff = 1.e-6;
else
vMsg("%s: unexpected output unit %s, conversion factor fc not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fc - ff)/ff > 0.0001) eX(9);
}
}
else if (!strcmp(pr->unit, "g")) {
double ff = -1.;
if (!strcmp(pr->uc, "g/m3"))
ff = 1.;
else if (!strcmp(pr->uc, "mg/m3"))
ff = 1.e3;
else if (!strcmp(pr->uc, "ug/m3"))
ff = 1.e6;
else if (!strcmp(pr->uc, "ng/m3"))
ff = 1.e9;
else if (!strcmp(pr->uc, "pg/m3"))
ff = 1.e12;
else
vMsg("%s: unexpected output unit %s, conversion factor fc not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fc - ff)/ff > 0.0001) eX(10);
}
ff = -1.;
if (!strcmp(pr->un, "g/(m2*s)"))
ff = 1.;
else if (!strcmp(pr->un, "mg/(m2*s)"))
ff = 1.e3;
else if (!strcmp(pr->un, "ug/(m2*s)"))
ff = 1.e6;
else if (!strcmp(pr->un, "ng/(m2*s)"))
ff = 1.e9;
else if (!strcmp(pr->un, "pg/(m2*s)"))
ff = 1.e12;
else if (!strcmp(pr->un, "g/(m2*d)"))
ff = 8.64e4;
else if (!strcmp(pr->un, "mg/(m2*d)"))
ff = 8.64e7;
else if (!strcmp(pr->un, "ug/(m2*d)"))
ff = 8.64e10;
else if (!strcmp(pr->un, "ng/(m2*d)"))
ff = 8.64e13;
else if (!strcmp(pr->un, "pg/(m2*d)"))
ff = 8.64e16;
else if (!strcmp(pr->un, "kg/(ha*a)"))
ff = 3.1536e8;
else if (!strcmp(pr->un, "g/(ha*a)"))
ff = 3.1536e11;
else if (!strcmp(pr->un, "mg/(ha*a)"))
ff = 3.1536e14;
else
vMsg("%s: unexpected output unit %s, conversion factor fn not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fn - ff)/ff > 0.0001) eX(11);
}
}
else
vMsg("%s: unexpected base unit %s, conversion factors not checked",
pr->name, pr->unit);
}
return n;
eX_1: eX_2:
eMSG("improper argument");
eX_3:
eMSG("buffer overflow in line %d", n+nn);
eX_4:
eMSG("parse error in line %d", n+nn);
eX_5:
eMSG("unit must be \"Bq\" in artm-mode");
eX_6:
eMSG("unit \"Bq\" allowed in artm-mode only");
eX_8: eX_9: eX_10:
eMSG("%s: wrong conversion factor fc for unit %s: %e", pr->name, pr->uc, pr->fc);
eX_11:
eMSG("%s: wrong conversion factor fn for unit %s: %e", pr->name, pr->un, pr->fn);
}
//=============================================================== SttGetSpecies
STTSPCREC *SttGetSpecies( // get the species definition record
char *name) // species name
{
STTSPCREC *pr;
int i;
if (name == NULL) return NULL;
pr = NULL;
for (i=0; i pr = SttSpcTab + i;
if (!strcmp(pr->name, name))
break;
pr = NULL;
}
return pr;
}
//===================================================================== SttRead
int SttRead( // read the settings for AUSTAL
char *path, // home directory of AUSTAL
char *pgm ) // program name
{
dP(SttRead);
FILE *f;
int n=0, ns=0, m=0, i=0, k, k1, k2, a1, a2, iodor;
char buf[buflen], name[256], sec[buflen], *pc, **ppc;
float fr_last, fr;
STTSPCREC *pr;
if (CHECK) vMsg("SttRead(%s, %s) ...", path, pgm);
*buf = 0;
*sec = 0;
SttMode = (0 == strncmp(pgm, "artm", 4));
sprintf(name, "%s/%s.%s", path, pgm, "settings");
//
f = fopen(path, "rb"); //bp
if (!f) {
vMsg("can't read file \"%s\"!", path); //bp
exit(2);
}
//
// scan the file
//
n = 0;
ns = 0;
while (fgets(buf, buflen, f)) {
n++;
if (strlen(buf) >= buflen-2) eX(1);
if (buf[0] != '[')
continue;
if (buf[1] == '.')
continue;
ns++;
}
if (CHECK) vMsg("%d species definitions found", ns);
SttSpcTab = ALLOC((ns+1)*sizeof(STTSPCREC));
SttSpcCount = ns;
//
// read the file
//
fseek(f, 0, SEEK_SET);
n = 0;
ns = 0;
*buf = 0;
while (1) {
if (!*buf) {
pc = fgets(buf, buflen, f);
if (pc == NULL)
break;
n++;
if (strlen(buf) >= buflen-2) eX(2);
trim(buf);
if (!*buf)
continue;
}
if (*buf != '[')
continue;
strcpy(sec, buf);
if (sec[1] == '.') {
if (!strcmp(sec, "[.astl]")) {
if (SttMode) eX(10);
pr = &SttAstlDefault;
}
else if (!strcmp(sec, "[.artm]")) {
if (!SttMode) eX(11);
pr = &SttArtmDefault;
}
else if (!strcmp(sec, "[.system]")) { //-2005-10-26
pr = NULL;
}
else eX(4);
}
else {
pr = SttSpcTab + ns;
ns++;
}
m = read_section(f, buf, n, pr);
if (m < 0) eX(3);
n += m;
}
fclose(f);
if (scanning) eX(5);
//
// find all species names
//
n = 0;
for (i=0; i pr = SttSpcTab + i;
if (!*(pr->grps)) {
strcpy(pr->grps, "0-0");
k1 = 0;
k2 = 0;
}
else {
pc = pr->grps;
if (strlen(pc) != 3 || !isdigit(pc[0]) || pc[1] != '-'
|| !isdigit(pc[2])) eX(6);
k1 = pc[0] - '0';
k2 = pc[2] - '0';
if (k1 < 0 || k2 > 5 || k1 > k2) eX(7);
//
// ignore hourly evaluation for substances with PM fractions because
// adding of pm-1 and pm-2 is not implemented for hourly means
// -2014-11-14
if (k2 > 0 && pr->rh > 0.0) {
pr->rh = 0.0;
vMsg("%s with size fractions: hourly evaluation not implemented, deactivated.", pr->name);
}
}
n += k2 - k1 + 1;
//
// components added to concentration -2024-01-17
if (!*(pr->cadd))
strcpy(pr->cadd, "0-2");
pc = pr->cadd;
a1 = pc[0] - '0';
a2 = pc[2] - '0';
if (a2 < a1) eX(71);
if (a1 < k1)
a1 = k1;
if (a2 > k2)
a2 = k2;
sprintf(pr->cadd, "%c%c%c", '0'+a1, '-', '0'+a2);
//
// check consistency of deposition parameters -2011-12-13
if (pr->rn > 0) { // logging of deposition requested
if (k2 == 0 && pr->vd <= 0 && pr->wf <= 0) eX(12);
}
else { // deposition not logged
// commented out uj -2019-02-07
// with the new TA Luft, one may be interested in the concentration
// but not deposition of a depositing material (e.g. PM2.5)
//if (k2 > 0 || pr->vd > 0 || pr->wf > 0) eX(13);
}
}
//
// some checks for odorants -2024-01-17
iodor = -1;
fr_last = -1;
for (i=0; i pr = SttSpcTab + i;
if (!strcmp(pr->name, "odor"))
iodor = i;
if (iodor < 0 && !strncmp(pr->name, "odor", 4)) eX(21);
if (iodor >= 0 && strncmp(pr->name, "odor", 4)) eX(22);
if (iodor < 0)
continue;
pc = pr->grps;
k1 = pc[0] - '0';
k2 = pc[2] - '0';
if (k1 != 0 || k2 != 0) eX(23);
if (i == iodor)
continue;
pc = strchr(pr->name, '_');
if (!pc || 1 != sscanf(pc+1, "%f", &fr)) eX(24);
if (fr < 1 || fr > 999) eX(25);
if (fr_last > 0 && fr <= fr_last) eX(26);
fr_last = fr;
}
//
//
if (CHECK) vMsg("%d components found", n);
SttCmpCount = n;
SttCmpNames = ALLOC((n+1)*(16 + sizeof(char*)));
ppc = SttCmpNames;
pc = (char*)(&ppc[n+1]);
for (i=0; i ppc[i] = pc + i*16;
ppc[n] = NULL;
n = 0;
for (i=0; i pr = SttSpcTab + i;
k1 = pr->grps[0] - '0';
k2 = pr->grps[2] - '0';
for (k=k1; k<=k2; k++) {
sprintf(SttCmpNames[n], "%s%s", pr->name, SttGrpXten[k]);
n++;
}
}
if (CHECK) for (i=0; i printf("%3d %s\n", i+1, SttCmpNames[i]);
return ns;
eX_1: eX_2:
eMSG("buffer overflow at line %d!", n);
eX_3:
eMSG("read error at section %s (line %d)!", sec, n);
eX_4:
eMSG("unknown section type %s (line %d)!", sec, n);
eX_5:
eMSG("can't read settings!");
eX_6: eX_7:
eMSG("groups \"%s\" invalid for species %s", pr->grps, pr->name);
eX_71:
eMSG("cadd \"%s\" invalid for species %s", pr->cadd, pr->name);
eX_10:
eMSG("section [.astl] not allowed in artm-mode!");
eX_11:
eMSG("section [.artm] not allowed in astl-mode!");
eX_12: // eX_13:
eMSG("inconsistent deposition parameters for species %s!", pr->name);
eX_21:
eMSG("rated odorants may be defined only after plain species odor!");
eX_22:
eMSG("after plain species odor only rated odotrants may be defined!");
eX_23:
eMSG("odorants may only contain a gaseous component!");
eX_24:
eMSG("can't evaluate factor for rated odorant %s!", pr->name);
eX_25:
eMSG("invalid factor for rated odorant %s!", pr->name);
eX_26:
eMSG("rated odorants must be sorted by increasing factor (%s)!", pr->name);
}
#ifdef MAIN //##########################################################
static char Path[256];
//================================================================== main
int main( int argc, char *argv[] ) {
char lfile[256];
int n;
if (argc < 2) {
printf("usage: TstStt\n");
exit(0);
}
strcpy(Path, argv[1]);
strcpy(lfile, Path);
strcat(lfile, "/TstStt.log");
MsgFile = fopen(lfile, "w");
MsgVerbose = 1;
MsgBreak = '\'';
vMsg("TstStt (%s %s)", __DATE__, __TIME__);
n = SttSpcRead(Path, 1);
if (n < 0) vMsg("Programm TstStt wegen eines Fehlers abgebrochen!");
else vMsg("Programm TstStt normal beendet.");
if (MsgFile) fclose(MsgFile);
return 0;
}
#endif //#################################################################
//=========================================================================
//
// History:
//
// 2005-09-12 2.3.2 lj vs, vd in m/s; wf in 1/s
// 2011-11-23 2.6.0 lj adapted from ARTM
// 2011-12-06 lj rain intensities added
// 2011-12-14 lj check of deposition parameters
// 2014-11-14 2.6.12 uj suppress hourly evaluation of pm (adding 1 and 2 not implemented)
// 2022-01-31 3.1.4 uj conversion factor checks
// 2024-01-17 3.3.0 uj removal of some deprecated parts
// variable number of rated odorants and checks
// flexible specification of added components
//
//==========================================================================
*
* Read Settings for ARTM, re-imported to AUSTAL
* =============================================
*
* Copyright (C) Umweltbundesamt, Dessau-Roßlau, Germany, 2002-2024
* Copyright (C) Janicke Consulting, 88662 Überlingen, Germany, 2002-2024
* Email: info@austal.de
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* last change: 2024-01-17 uj
*
*============================================================================*/
char *TalSttVersion = "3.3.0";
static char *eMODn = "TalStt";
static int CHECK = 0;
#include
#include
#include
#include
#include
#include
#include "IBJmsg.h"
#include "IBJary.h"
#include "IBJtxt.h"
#include "IBJdmn.h"
#include "TalStt.h"
typedef struct {
char *_name; // parameter name + buffer for tokens
int nTokens; // number of tokens
char **_tokens; // array of pointers to tokens
double *_dd; // array of values or NULL
} INPREC;
char **SttCmpNames; // names of emittable components
STTSPCREC *SttSpcTab; // species defined
STTSPCREC SttAstlDefault;
STTSPCREC SttArtmDefault;
int SttSpcCount, SttCmpCount, SttTypeCount, SttMode;
char *SttGroups[STTGRPSIZE] = { "gas", "gas", "gas", "pm3", "pm4", "pmu" };
char *SttGrpXten[STTGRPSIZE] = { "", "-1", "-2", "-3", "-4", "-u" };
double SttVdVec[STTGRPSIZE] = { 0, 0.001, 0.01, 0.05, 0.20, 0.07 }; //-2005-09-12
double SttVsVec[STTGRPSIZE] = { 0, 0.000, 0.00, 0.04, 0.15, 0.06 }; //-2005-09-12
double SttWfVec[STTGRPSIZE] = { 0, 1.0e-5, 2.0e-5, 3.0e-5, 4.0e-5, 5.0e-5 }; //-2005-09-12
double SttWeVec[STTGRPSIZE] = { 0, 0.6, 0.6, 0.6, 0.6, 0.6 }; //-2005-04-12
//char SttRiSep[256] = "NIEDERSCHLAG:"; //-2011-12-06 -2024-01-17
double SttRiVec[1] = { 0 }; // deprecated, AKS with rain -2024-01-17
int SttNstat = 1;
double SttNoxTimes[6] = { 2.9, 2.5, 1.9, 1.3, 0.9, 0.3 };
double SttHmMean[6] = { 0, 0, 0, 800, 1100, 1100 };
double SttOdorThreshold = 0.25;
double SttSrcTurbulence = 0.1;
double SttGmmB1 = 1.00;
double SttGmmB2 = 0.14286;
char SttGmmUnit[16] = "Bq/m²";
double SttGmmFd = 4;
double SttGmmFe = 3;
double SttGmmFf = 10;
double SttGmmFr = 10;
double SttGmmMue = 0.0082;
double SttGmmRef = 1.e-6;
double SttGmmMu[2] = { 7.78e-3, 1.82e-2 };
double SttGmmDa[2][6] = {
{ 1.0, 0.77, 0.35, -4.00e-2, 3.20e-3, -8.20e-5 },
{ 1.0, 1.92, 1.74, -3.39e-2, 3.86e-2, -2.11e-3 }
};
double SttGmmBk[2][4][4] = {
{ { 0.485, 0.064, 1.705, -1.179 },
{ 0.137, 1.878, -4.817, 2.883 },
{ -0.0035, -0.8569, 2.0527, -1.2552 },
{ -0.0018, 0.0997, -0.2392, 0.1503 } },
{ { 0.279, 0.595, -0.205, 0.622 },
{ 0.135, 0.866, -0.716, -0.578 },
{ -0.0131, -0.324, 0.1103, 0.2892 },
{ 0.0003, 0.0313, -0.0017, -0.0337 } }
};
static char cbreak = '#';
static int scanning = 0;
#define buflen 256
static void trim(char *buf) {
int n, i, quote=0;
if (!buf) return;
n = strlen(buf);
for (i=0; i
if (i > 0 && buf[i-1] == '\\') continue;
quote = (quote == 0);
continue;
}
if (quote) continue;
if (buf[i] == cbreak) {
buf[i] = 0;
n = i;
break;
}
}
for (i=n-1; i>=0; i--) {
if (buf[i] <= ' ') buf[i] = 0;
else break;
}
}
static int starts_with(char *source, char *s) {
int rc;
if (!source || !s) return 0;
rc = (0 == strncmp(source, s, strlen(s)));
return rc;
}
static int ends_with(char *source, char *s) {
int rc, l;
if (!source || !s) return 0;
l = strlen(source) - strlen(s);
if (l < 0) return 0;
rc = (0 == strcmp(source+l, s));
return rc;
}
//================================================================= parse
static int parse( char *line, INPREC *pp ) {
dP(parse);
int i, n, l;
int is_number = 1;
char *p1, c, tk[256];
if (!pp) eX(2);
pp->_name = NULL;
pp->nTokens = 0;
pp->_tokens = NULL;
pp->_dd = NULL;
if (line==NULL || !*line)
return 0;
l = strlen(line);
pp->_name = ALLOC(l+1); if (!pp->_name) eX(1);
strcpy(pp->_name, line);
for (p1=pp->_name; isalnum(*p1) || strchr("()", *p1); p1++);
if (!*p1)
return 0;
*p1++ = 0;
if (!*pp->_name)
return 0;
pp->_tokens = _MsgTokens(p1, " ;\t\r\n=");
if (!pp->_tokens)
return 0;
for (n=0; ; n++)
if (!pp->_tokens[n])
break;
if (n == 0)
return 0;
pp->nTokens = n;
for (i=0; i
if (!isdigit(c) && c!='-' && c!='+')
is_number = 0;
}
if (is_number) {
char *tail;
pp->_dd = ALLOC(n*sizeof(double));
for (i=0; i
tk[255] = 0;
for (p1=tk; (*p1); p1++) if (*p1 == ',') *p1 = '.'; //-2003-07-07
pp->_dd[i] = strtod(tk, &tail);
if (*tail) {
FREE(pp->_dd);
pp->_dd = NULL;
is_number = 0;
break;
}
}
}
if (!is_number) {
for (i=0; i
}
return is_number;
eX_1: eX_2:
eMSG("Internal error!");
}
static char *set_string(char *s, INPREC inp) {
static char msg[256];
if (CHECK) vMsg("setting string %s", inp._name);
if (inp.nTokens < 1) {
sprintf(msg, "no string data for %s", inp._name);
return msg;
}
if (!inp._tokens) {
sprintf(msg, "missing string for %s", inp._name);
return msg;
}
if (strlen(inp._tokens[0]) > 15) {
sprintf(msg, "string too long for %s", inp._name);
return msg;
}
strcpy(s, inp._tokens[0]);
return NULL;
}
static char *set_float(float *pf, INPREC inp) {
static char msg[256];
if (CHECK) vMsg("setting float %s", inp._name);
if (inp.nTokens < 1) {
sprintf(msg, "no float data for %s", inp._name);
return msg;
}
if (!inp._dd) {
sprintf(msg, "missing float for %s", inp._name);
return msg;
}
*pf = (float)inp._dd[0];
return NULL;
}
static char *set_doubles(double *pf, INPREC inp, int n) {
static char msg[256];
int i;
if (CHECK) vMsg("setting doubles %s", inp._name);
if (!inp._dd) {
sprintf(msg, "missing doubles for %s", inp._name);
return msg;
}
if (n > inp.nTokens) n = inp.nTokens;
for (i=0; i
return NULL;
}
static char *set_integer(int *pi, INPREC inp) {
static char msg[256];
if (CHECK) vMsg("setting integer %s", inp._name);
if (inp.nTokens < 1) {
sprintf(msg, "no integer data for %s", inp._name);
return msg;
}
if (!inp._dd) {
sprintf(msg, "missing integer for %s", inp._name);
return msg;
}
*pi = (int)inp._dd[0];
return NULL;
}
#define TRY_STRING(a) if (!strcmp(inp._name, #a)) msg = set_string(pr->a, inp)
#define TRY_FLOAT(a) if (!strcmp(inp._name, #a)) msg = set_float(&(pr->a), inp)
#define TRY_INTEGER(a) if (!strcmp(inp._name, #a)) msg = set_integer(&(pr->a), inp)
#define TRY_DOUBLES(a,b,c) if (!strcmp(inp._name, #a)) msg = set_doubles((b), inp, (c))
#define TRY_TEXT(a,b) if (!strcmp(inp._name, #a)) msg = set_string((b), inp)
static char *analyse_spec(INPREC inp, STTSPCREC *pr) {
char *msg = NULL;
if (CHECK) vMsg("analyse spec: name=%s, section=%s", inp._name, pr->name);
TRY_STRING(grps);
else TRY_STRING(cadd); //-2024-01-17
else TRY_STRING(unit);
else TRY_FLOAT(vd);
else TRY_FLOAT(wf);
else TRY_FLOAT(we);
else TRY_FLOAT(de);
else TRY_FLOAT(fr);
else TRY_FLOAT(fc);
else TRY_STRING(uc);
else TRY_FLOAT(fn);
else TRY_STRING(un);
else TRY_FLOAT(ry);
else TRY_INTEGER(dy);
else TRY_INTEGER(nd);
else TRY_FLOAT(rd);
else TRY_INTEGER(dd);
else TRY_INTEGER(nh);
else TRY_FLOAT(rh);
else TRY_INTEGER(dh);
else TRY_FLOAT(rn);
else TRY_INTEGER(dn);
else msg = "unknown parameter";
return msg;
}
static char *analyse_astl(INPREC inp) {
char *msg = NULL;
TRY_DOUBLES(odorthreshold, &SttOdorThreshold, 1);
else TRY_DOUBLES(srcturbulence, &SttSrcTurbulence, 1);
else TRY_DOUBLES(noxtimes, SttNoxTimes, 6);
else msg = "unknown astl-parameter";
return msg;
}
static char *analyse_artm(INPREC inp) {
char *msg = NULL;
TRY_TEXT(gmunit, SttGmmUnit);
else TRY_DOUBLES(gmref, &SttGmmRef, 1);
else TRY_DOUBLES(gmfd, &SttGmmFd, 1);
else TRY_DOUBLES(gmfe, &SttGmmFe, 1);
else TRY_DOUBLES(gmff, &SttGmmFf, 1);
else TRY_DOUBLES(gmfr, &SttGmmFr, 1);
else TRY_DOUBLES(gmmu10, &SttGmmMu[0], 1);
else TRY_DOUBLES(gmmu01, &SttGmmMu[1], 1);
else TRY_DOUBLES(gmda10, SttGmmDa[0], 6);
else TRY_DOUBLES(gmda01, SttGmmDa[1], 6);
else TRY_DOUBLES(gmbk10(0), SttGmmBk[0][0], 4);
else TRY_DOUBLES(gmbk10(1), SttGmmBk[0][1], 4);
else TRY_DOUBLES(gmbk10(2), SttGmmBk[0][2], 4);
else TRY_DOUBLES(gmbk10(3), SttGmmBk[0][3], 4);
else TRY_DOUBLES(gmbk01(0), SttGmmBk[1][0], 4);
else TRY_DOUBLES(gmbk01(1), SttGmmBk[1][1], 4);
else TRY_DOUBLES(gmbk01(2), SttGmmBk[1][2], 4);
else TRY_DOUBLES(gmbk01(3), SttGmmBk[1][3], 4);
else msg = "unknown artm-parameter";
return msg;
}
static char *analyse_system(INPREC inp) {
char *msg = NULL;
TRY_DOUBLES(vdvec, SttVdVec+1, 5);
else TRY_DOUBLES(vsvec, SttVsVec+1, 5);
else TRY_DOUBLES(wfvec, SttWfVec+1, 5);
else TRY_DOUBLES(wevec, SttWeVec+1, 5);
//else TRY_DOUBLES(rivec, SttRiVec, 6); //-2011-12-06 -2024-01-17
else TRY_DOUBLES(hmmean, SttHmMean, 6);
//else if (!strcmp(inp._name, "risep")) //-2011-12-06 -2024-01-17
// msg = set_string(SttRiSep, inp);
else msg = "unknown system-parameter";
return msg;
}
#undef TRY_STRING
#undef TRY_FLOAT
#undef TRY_DOUBLES
#undef TRY_INTEGER
#undef TRY_TEXT
static int read_section(FILE *f, char *buf, int nn, STTSPCREC *pr) {
dP(read_section);
int n=0, l, i;
char *pc, *msg, name[16];
INPREC inp;
if (f==NULL || buf==NULL) eX(1);
l = strlen(buf);
if (l < 2 || l > 17) eX(2);
strncpy(name, buf+1, l-2);
MsgLow(name);
name[l-2] = 0;
if (pr) {
if (SttMode) {
memcpy(pr, &SttArtmDefault, sizeof(STTSPCREC));
}
else {
memcpy(pr, &SttAstlDefault, sizeof(STTSPCREC));
}
strcpy(pr->name, name);
}
// pr->wf = -1;
// pr->wc = -1;
while(1) {
*buf = 0;
pc = fgets(buf, buflen, f);
if (pc == NULL)
break;
n++;
if (strlen(buf) >= buflen-2) eX(3);
trim(buf);
if (!*buf)
continue;
if (*buf == '[')
break;
if (0 > parse(buf, &inp)) eX(4);
if (CHECK) vMsg("> %s", buf);
MsgLow(inp._name);
if (pr == NULL) { //-2005-10-26
msg = analyse_system(inp);
}
else {
msg = analyse_spec(inp, pr);
if (msg && starts_with(msg, "unknown parameter")) {
if (!strcmp(pr->name, ".astl"))
msg = analyse_astl(inp);
else if (!strcmp(pr->name, ".artm"))
msg = analyse_artm(inp);
}
}
FREE(inp._name);
FREE(inp._tokens);
if (inp._dd != NULL) {
FREE(inp._dd);
inp._dd = NULL;
}
if (msg && *msg) {
vMsg("error in line %d: %s\n%s", n+nn, buf, msg);
scanning = 1;
}
}
//
if (pr) {
if (!strncmp(pr->name, "no", 2)) pr->de = 0; //-2005-09-01
if (SttMode) {
if (strcmp(pr->unit, "Bq")) eX(5);
}
else {
if (!strcmp(pr->unit, "Bq")) eX(6);
}
//
// basic unit checks //-2022-01-31
if (!strcmp(pr->unit, "OU")) {
double ff = -1.;
if (!strcmp(pr->uc, "%"))
ff = 100.;
else
vMsg("%s: unexpected output unit %s, conversion factor fc not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fc - ff)/ff > 0.0001) eX(8);
}
}
else if (!strcmp(pr->unit, "1")) {
double ff = -1.;
if (!strcmp(pr->uc, "1/m3"))
ff = 1.;
else if (!strcmp(pr->uc, "1/cm3"))
ff = 1.e-6;
else
vMsg("%s: unexpected output unit %s, conversion factor fc not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fc - ff)/ff > 0.0001) eX(9);
}
}
else if (!strcmp(pr->unit, "g")) {
double ff = -1.;
if (!strcmp(pr->uc, "g/m3"))
ff = 1.;
else if (!strcmp(pr->uc, "mg/m3"))
ff = 1.e3;
else if (!strcmp(pr->uc, "ug/m3"))
ff = 1.e6;
else if (!strcmp(pr->uc, "ng/m3"))
ff = 1.e9;
else if (!strcmp(pr->uc, "pg/m3"))
ff = 1.e12;
else
vMsg("%s: unexpected output unit %s, conversion factor fc not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fc - ff)/ff > 0.0001) eX(10);
}
ff = -1.;
if (!strcmp(pr->un, "g/(m2*s)"))
ff = 1.;
else if (!strcmp(pr->un, "mg/(m2*s)"))
ff = 1.e3;
else if (!strcmp(pr->un, "ug/(m2*s)"))
ff = 1.e6;
else if (!strcmp(pr->un, "ng/(m2*s)"))
ff = 1.e9;
else if (!strcmp(pr->un, "pg/(m2*s)"))
ff = 1.e12;
else if (!strcmp(pr->un, "g/(m2*d)"))
ff = 8.64e4;
else if (!strcmp(pr->un, "mg/(m2*d)"))
ff = 8.64e7;
else if (!strcmp(pr->un, "ug/(m2*d)"))
ff = 8.64e10;
else if (!strcmp(pr->un, "ng/(m2*d)"))
ff = 8.64e13;
else if (!strcmp(pr->un, "pg/(m2*d)"))
ff = 8.64e16;
else if (!strcmp(pr->un, "kg/(ha*a)"))
ff = 3.1536e8;
else if (!strcmp(pr->un, "g/(ha*a)"))
ff = 3.1536e11;
else if (!strcmp(pr->un, "mg/(ha*a)"))
ff = 3.1536e14;
else
vMsg("%s: unexpected output unit %s, conversion factor fn not checked",
pr->name, pr->uc);
if (ff > 0.) {
if (fabs(pr->fn - ff)/ff > 0.0001) eX(11);
}
}
else
vMsg("%s: unexpected base unit %s, conversion factors not checked",
pr->name, pr->unit);
}
return n;
eX_1: eX_2:
eMSG("improper argument");
eX_3:
eMSG("buffer overflow in line %d", n+nn);
eX_4:
eMSG("parse error in line %d", n+nn);
eX_5:
eMSG("unit must be \"Bq\" in artm-mode");
eX_6:
eMSG("unit \"Bq\" allowed in artm-mode only");
eX_8: eX_9: eX_10:
eMSG("%s: wrong conversion factor fc for unit %s: %e", pr->name, pr->uc, pr->fc);
eX_11:
eMSG("%s: wrong conversion factor fn for unit %s: %e", pr->name, pr->un, pr->fn);
}
//=============================================================== SttGetSpecies
STTSPCREC *SttGetSpecies( // get the species definition record
char *name) // species name
{
STTSPCREC *pr;
int i;
if (name == NULL) return NULL;
pr = NULL;
for (i=0; i
if (!strcmp(pr->name, name))
break;
pr = NULL;
}
return pr;
}
//===================================================================== SttRead
int SttRead( // read the settings for AUSTAL
char *path, // home directory of AUSTAL
char *pgm ) // program name
{
dP(SttRead);
FILE *f;
int n=0, ns=0, m=0, i=0, k, k1, k2, a1, a2, iodor;
char buf[buflen], name[256], sec[buflen], *pc, **ppc;
float fr_last, fr;
STTSPCREC *pr;
if (CHECK) vMsg("SttRead(%s, %s) ...", path, pgm);
*buf = 0;
*sec = 0;
SttMode = (0 == strncmp(pgm, "artm", 4));
sprintf(name, "%s/%s.%s", path, pgm, "settings");
//
f = fopen(path, "rb"); //bp
if (!f) {
vMsg("can't read file \"%s\"!", path); //bp
exit(2);
}
//
// scan the file
//
n = 0;
ns = 0;
while (fgets(buf, buflen, f)) {
n++;
if (strlen(buf) >= buflen-2) eX(1);
if (buf[0] != '[')
continue;
if (buf[1] == '.')
continue;
ns++;
}
if (CHECK) vMsg("%d species definitions found", ns);
SttSpcTab = ALLOC((ns+1)*sizeof(STTSPCREC));
SttSpcCount = ns;
//
// read the file
//
fseek(f, 0, SEEK_SET);
n = 0;
ns = 0;
*buf = 0;
while (1) {
if (!*buf) {
pc = fgets(buf, buflen, f);
if (pc == NULL)
break;
n++;
if (strlen(buf) >= buflen-2) eX(2);
trim(buf);
if (!*buf)
continue;
}
if (*buf != '[')
continue;
strcpy(sec, buf);
if (sec[1] == '.') {
if (!strcmp(sec, "[.astl]")) {
if (SttMode) eX(10);
pr = &SttAstlDefault;
}
else if (!strcmp(sec, "[.artm]")) {
if (!SttMode) eX(11);
pr = &SttArtmDefault;
}
else if (!strcmp(sec, "[.system]")) { //-2005-10-26
pr = NULL;
}
else eX(4);
}
else {
pr = SttSpcTab + ns;
ns++;
}
m = read_section(f, buf, n, pr);
if (m < 0) eX(3);
n += m;
}
fclose(f);
if (scanning) eX(5);
//
// find all species names
//
n = 0;
for (i=0; i
if (!*(pr->grps)) {
strcpy(pr->grps, "0-0");
k1 = 0;
k2 = 0;
}
else {
pc = pr->grps;
if (strlen(pc) != 3 || !isdigit(pc[0]) || pc[1] != '-'
|| !isdigit(pc[2])) eX(6);
k1 = pc[0] - '0';
k2 = pc[2] - '0';
if (k1 < 0 || k2 > 5 || k1 > k2) eX(7);
//
// ignore hourly evaluation for substances with PM fractions because
// adding of pm-1 and pm-2 is not implemented for hourly means
// -2014-11-14
if (k2 > 0 && pr->rh > 0.0) {
pr->rh = 0.0;
vMsg("%s with size fractions: hourly evaluation not implemented, deactivated.", pr->name);
}
}
n += k2 - k1 + 1;
//
// components added to concentration -2024-01-17
if (!*(pr->cadd))
strcpy(pr->cadd, "0-2");
pc = pr->cadd;
a1 = pc[0] - '0';
a2 = pc[2] - '0';
if (a2 < a1) eX(71);
if (a1 < k1)
a1 = k1;
if (a2 > k2)
a2 = k2;
sprintf(pr->cadd, "%c%c%c", '0'+a1, '-', '0'+a2);
//
// check consistency of deposition parameters -2011-12-13
if (pr->rn > 0) { // logging of deposition requested
if (k2 == 0 && pr->vd <= 0 && pr->wf <= 0) eX(12);
}
else { // deposition not logged
// commented out uj -2019-02-07
// with the new TA Luft, one may be interested in the concentration
// but not deposition of a depositing material (e.g. PM2.5)
//if (k2 > 0 || pr->vd > 0 || pr->wf > 0) eX(13);
}
}
//
// some checks for odorants -2024-01-17
iodor = -1;
fr_last = -1;
for (i=0; i
if (!strcmp(pr->name, "odor"))
iodor = i;
if (iodor < 0 && !strncmp(pr->name, "odor", 4)) eX(21);
if (iodor >= 0 && strncmp(pr->name, "odor", 4)) eX(22);
if (iodor < 0)
continue;
pc = pr->grps;
k1 = pc[0] - '0';
k2 = pc[2] - '0';
if (k1 != 0 || k2 != 0) eX(23);
if (i == iodor)
continue;
pc = strchr(pr->name, '_');
if (!pc || 1 != sscanf(pc+1, "%f", &fr)) eX(24);
if (fr < 1 || fr > 999) eX(25);
if (fr_last > 0 && fr <= fr_last) eX(26);
fr_last = fr;
}
//
//
if (CHECK) vMsg("%d components found", n);
SttCmpCount = n;
SttCmpNames = ALLOC((n+1)*(16 + sizeof(char*)));
ppc = SttCmpNames;
pc = (char*)(&ppc[n+1]);
for (i=0; i
ppc[n] = NULL;
n = 0;
for (i=0; i
k1 = pr->grps[0] - '0';
k2 = pr->grps[2] - '0';
for (k=k1; k<=k2; k++) {
sprintf(SttCmpNames[n], "%s%s", pr->name, SttGrpXten[k]);
n++;
}
}
if (CHECK) for (i=0; i
return ns;
eX_1: eX_2:
eMSG("buffer overflow at line %d!", n);
eX_3:
eMSG("read error at section %s (line %d)!", sec, n);
eX_4:
eMSG("unknown section type %s (line %d)!", sec, n);
eX_5:
eMSG("can't read settings!");
eX_6: eX_7:
eMSG("groups \"%s\" invalid for species %s", pr->grps, pr->name);
eX_71:
eMSG("cadd \"%s\" invalid for species %s", pr->cadd, pr->name);
eX_10:
eMSG("section [.astl] not allowed in artm-mode!");
eX_11:
eMSG("section [.artm] not allowed in astl-mode!");
eX_12: // eX_13:
eMSG("inconsistent deposition parameters for species %s!", pr->name);
eX_21:
eMSG("rated odorants may be defined only after plain species odor!");
eX_22:
eMSG("after plain species odor only rated odotrants may be defined!");
eX_23:
eMSG("odorants may only contain a gaseous component!");
eX_24:
eMSG("can't evaluate factor for rated odorant %s!", pr->name);
eX_25:
eMSG("invalid factor for rated odorant %s!", pr->name);
eX_26:
eMSG("rated odorants must be sorted by increasing factor (%s)!", pr->name);
}
#ifdef MAIN //##########################################################
static char Path[256];
//================================================================== main
int main( int argc, char *argv[] ) {
char lfile[256];
int n;
if (argc < 2) {
printf("usage: TstStt
exit(0);
}
strcpy(Path, argv[1]);
strcpy(lfile, Path);
strcat(lfile, "/TstStt.log");
MsgFile = fopen(lfile, "w");
MsgVerbose = 1;
MsgBreak = '\'';
vMsg("TstStt (%s %s)", __DATE__, __TIME__);
n = SttSpcRead(Path, 1);
if (n < 0) vMsg("Programm TstStt wegen eines Fehlers abgebrochen!");
else vMsg("Programm TstStt normal beendet.");
if (MsgFile) fclose(MsgFile);
return 0;
}
#endif //#################################################################
//=========================================================================
//
// History:
//
// 2005-09-12 2.3.2 lj vs, vd in m/s; wf in 1/s
// 2011-11-23 2.6.0 lj adapted from ARTM
// 2011-12-06 lj rain intensities added
// 2011-12-14 lj check of deposition parameters
// 2014-11-14 2.6.12 uj suppress hourly evaluation of pm (adding 1 and 2 not implemented)
// 2022-01-31 3.1.4 uj conversion factor checks
// 2024-01-17 3.3.0 uj removal of some deprecated parts
// variable number of rated odorants and checks
// flexible specification of added components
//
//==========================================================================