--- ray/src/util/wrapBSDF.c 2015/02/11 18:08:10 2.1 +++ ray/src/util/wrapBSDF.c 2015/02/16 19:11:28 2.7 @@ -1,10 +1,10 @@ #ifndef lint -static const char RCSid[] = "$Id: wrapBSDF.c,v 2.1 2015/02/11 18:08:10 greg Exp $"; +static const char RCSid[] = "$Id: wrapBSDF.c,v 2.7 2015/02/16 19:11:28 greg Exp $"; #endif /* - * Wrap BSDF data in valid WINDOW XML wrapper + * Wrap BSDF data in valid WINDOW XML file * - * G. Ward January 2015 + * G. Ward February 2015 */ #include @@ -15,48 +15,54 @@ static const char RCSid[] = "$Id: wrapBSDF.c,v 2.1 201 #include "bsdf_m.h" /* XML template file names */ const char def_template[] = "minimalBSDFt.xml"; -const char win6_template[] = "window6BSDFt.xml"; +const char win6_template[] = "WINDOW6BSDFt.xml"; const char stdin_name[] = ""; - /* input files (can be stdin_name) */ const char *xml_input = NULL; /* unit for materials & geometry */ -const char *attr_unit = "millimeter"; +const char *attr_unit = "meter"; const char legal_units[] = "meter|foot|inch|centimeter|millimeter"; /* system materials & geometry */ const char *mgf_geometry = NULL; - /* angle basis */ + /* angle bases */ enum { ABdefault=-1, ABklemsFull=0, ABklemsHalf, ABklemsQuarter, ABtensorTree3, ABtensorTree4, ABend }; int angle_basis = ABdefault; +int correct_solid_angle = 0; + +const char *klems_basis_name[] = { + "LBNL/Klems Full", + "LBNL/Klems Half", + "LBNL/Klems Quarter", +}; /* field IDs and nicknames */ struct s_fieldID { char nickName[4]; - int has_unit; + short has_unit; + short win_need; const char *fullName; } XMLfieldID[] = { - {"m", 0, "Manufacturer"}, - {"n", 0, "Name"}, - {"c", 0, "ThermalConductivity"}, - {"ef", 0, "EmissivityFront"}, - {"eb", 0, "EmissivityBack"}, - {"tir", 0, "TIR"}, - {"eo", 0, "EffectiveOpennessFraction"}, - {"t", 1, "Thickness"}, - {"h", 1, "Height"}, - {"w", 1, "Width"}, - {"\0", -1, NULL} /* terminator */ + {"m", 0, 1, "Manufacturer"}, + {"n", 0, 1, "Name"}, + {"c", 0, 0, "ThermalConductivity"}, + {"ef", 0, 0, "EmissivityFront"}, + {"eb", 0, 0, "EmissivityBack"}, + {"tir", 0, 0, "TIR"}, + {"eo", 0, 0, "EffectiveOpennessFraction"}, + {"t", 1, 1, "Thickness"}, + {"h", 1, 0, "Height"}, + {"w", 1, 0, "Width"}, + {"\0", 0, 0, NULL} /* terminator */ }; /* field assignments */ #define MAXASSIGN 12 const char *field_assignment[MAXASSIGN]; int nfield_assign = 0; #define FASEP ';' - /* data file(s) & spectra */ enum { DTtransForward, DTtransBackward, DTreflForward, DTreflBackward }; @@ -76,26 +82,36 @@ const char *spectr_file[MAXFILES]; /* custom spectral const char top_level_name[] = "WindowElement"; -static char *basis_definition[] = { +static char basis_definition[][256] = { + "\t\n" "\t\tColumns\n" "\t\t\n" "\t\t\tLBNL/Klems Full\n" - "\t\t\n", + "\t\t\t\n" + "\t\n", + "\t\n" "\t\tColumns\n" "\t\t\n" "\t\t\tLBNL/Klems Half\n" - "\t\t\n", + "\t\t\t\n" + "\t\n", + "\t\n" "\t\tColumns\n" "\t\t\n" "\t\t\tLBNL/Klems Quarter\n" - "\t\t\n", + "\t\t\t\n" + "\t\n", - "\t\tTensorTree3\n", + "\t\n" + "\t\tTensorTree3\n" + "\t\n", - "\t\tTensorTree4\n", + "\t\n" + "\t\tTensorTree4\n" + "\t\n", }; /* Copy data from file descriptor to stdout and close */ @@ -130,11 +146,11 @@ input2str(const char *inpspec) return ""; if (inpspec == stdin_name) { /* read from stdin */ fp = stdin; - } else if (*inpspec == '!') { /* read from command */ + } else if (inpspec[0] == '!') { /* read from command */ fp = popen(inpspec+1, "r"); if (fp == NULL) { fprintf(stderr, "Cannot start process '%s'\n", - inpspec+1); + inpspec); return ""; } } else { /* else load file */ @@ -144,26 +160,24 @@ input2str(const char *inpspec) return ""; } len = lseek(fd, 0L, SEEK_END); - if (len < 0) { - fprintf(stderr, "%s: seek error\n", inpspec); + if (len > 0) { + lseek(fd, 0L, SEEK_SET); + str = (char *)malloc(len+1); + if (str == NULL) { + close(fd); + goto memerr; + } + if (read(fd, str, len) != len) { + fprintf(stderr, "%s: read error\n", inpspec); + free(str); + close(fd); + return ""; + } + str[len] = '\0'; close(fd); - return ""; + return str; } - lseek(fd, 0L, SEEK_SET); - str = (char *)malloc(len+1); - if (str == NULL) { - close(fd); - goto memerr; - } - if (read(fd, str, len) != len) { - fprintf(stderr, "%s: read error\n", inpspec); - free(str); - close(fd); - return ""; - } - str[len] = '\0'; - close(fd); - return str; + fp = fdopen(fd, "r"); /* not a regular file */ } /* reading from stream */ str = (char *)malloc((len=8192)+1); @@ -186,15 +200,15 @@ input2str(const char *inpspec) if (str == NULL) goto memerr; } - if (*inpspec != '!') + if (inpspec[0] != '!') fclose(fp); else if (pclose(fp)) - fprintf(stderr, "Error running command '%s'\n", inpspec+1); + fprintf(stderr, "Error running command '%s'\n", inpspec); return str; memerr: fprintf(stderr, "%s: error allocating memory\n", inpspec); if (fp != NULL) - (*inpspec == '!') ? pclose(fp) : fclose(fp); + (inpspec[0] == '!') ? pclose(fp) : fclose(fp); return ""; } @@ -204,8 +218,6 @@ mat_assignments(const char *caller, const char *fn, ez { int i; - if (!nfield_assign) - return 1; wtl = ezxml_child(wtl, "Material"); if (wtl == NULL) { fprintf(stderr, "%s: missing tag\n", fn); @@ -214,6 +226,7 @@ mat_assignments(const char *caller, const char *fn, ez for (i = 0; i < nfield_assign; i++) { const char *fnext = field_assignment[i]; for ( ; ; ) { + int added = 0; ezxml_t fld; char sbuf[512]; int j; @@ -222,7 +235,7 @@ mat_assignments(const char *caller, const char *fn, ez ++fnext; if (!*fnext) break; - for (j = 0; (*fnext != '=') & !isspace(*fnext); ) { + for (j = 0; *fnext != '=' && !isspace(*fnext); ) { if (!*fnext | (*fnext == FASEP) | (j >= sizeof(sbuf)-1)) { fprintf(stderr, @@ -232,21 +245,27 @@ mat_assignments(const char *caller, const char *fn, ez } sbuf[j++] = *fnext++; } - sbuf[j] = '\0'; /* check nick-names */ + sbuf[j] = '\0'; /* check known field */ for (j = 0; XMLfieldID[j].nickName[0]; j++) - if (!strcasecmp(sbuf, XMLfieldID[j].nickName)) { + if (!strcasecmp(sbuf, XMLfieldID[j].nickName) || + !strcasecmp(sbuf, XMLfieldID[j].fullName)) { strcpy(sbuf, XMLfieldID[j].fullName); break; } /* check if tag exists */ fld = ezxml_child(wtl, sbuf); if (fld == NULL) { /* otherwise, create one */ - fprintf(stderr, "%s: warning - adding tag <%s>\n", - fn, sbuf); + if (!XMLfieldID[j].nickName[0]) + fprintf(stderr, + "%s: warning - adding tag <%s>\n", + fn, sbuf); + ezxml_add_txt(wtl, "\t"); fld = ezxml_add_child_d(wtl, sbuf, strlen(wtl->txt)); + ++added; } if (XMLfieldID[j].has_unit) ezxml_set_attr(fld, "unit", attr_unit); + XMLfieldID[j].win_need = 0; while (isspace(*fnext)) ++fnext; if (*fnext++ != '=') { @@ -255,7 +274,7 @@ mat_assignments(const char *caller, const char *fn, ez caller, field_assignment[i]); return 0; } - for (j = 0; *fnext & (*fnext != FASEP); ) { + for (j = 0; *fnext && *fnext != FASEP; ) { if (j >= sizeof(sbuf)-1) { fprintf(stderr, "%s: field too long in '%s'\n", @@ -266,9 +285,22 @@ mat_assignments(const char *caller, const char *fn, ez } sbuf[j] = '\0'; ezxml_set_txt_d(fld, sbuf); + if (added) + ezxml_add_txt(wtl, "\n\t"); fnext += (*fnext == FASEP); } } + /* check required WINDOW settings */ + if (xml_input == win6_template) + for (i = 0; XMLfieldID[i].nickName[0]; i++) + if (XMLfieldID[i].win_need && + !ezxml_txt(ezxml_child(wtl,XMLfieldID[i].fullName))[0]) { + fprintf(stderr, + "%s: missing required '%s' assignment for WINDOW <%s>\n", + caller, XMLfieldID[i].nickName, + XMLfieldID[i].fullName); + return 0; + } return 1; /* no errors */ } @@ -297,14 +329,22 @@ finish_angle_basis(ezxml_t ab) sprintf(buf, "%g", i ? .5*(abase_list[n].lat[i].tmin + abase_list[n].lat[i+1].tmin) : .0); + ezxml_add_txt(abb, "\n\t\t\t\t"); ezxml_set_txt_d(ezxml_add_child(abb,"Theta",strlen(abb->txt)), buf); sprintf(buf, "%d", abase_list[n].lat[i].nphis); + ezxml_add_txt(abb, "\n\t\t\t\t"); ezxml_set_txt_d(ezxml_add_child(abb,"nPhis",strlen(abb->txt)), buf); + ezxml_add_txt(abb, "\n\t\t\t\t"); tb = ezxml_add_child(abb, "ThetaBounds", strlen(abb->txt)); + ezxml_add_txt(tb, "\n\t\t\t\t\t"); sprintf(buf, "%g", abase_list[n].lat[i].tmin); - ezxml_set_txt(ezxml_add_child(tb,"LowerTheta",strlen(tb->txt)), buf); + ezxml_set_txt_d(ezxml_add_child(tb,"LowerTheta",strlen(tb->txt)), buf); + ezxml_add_txt(tb, "\n\t\t\t\t\t"); sprintf(buf, "%g", abase_list[n].lat[i+1].tmin); - ezxml_set_txt(ezxml_add_child(tb,"UpperTheta",strlen(tb->txt)), buf); + ezxml_set_txt_d(ezxml_add_child(tb,"UpperTheta",strlen(tb->txt)), buf); + ezxml_add_txt(tb, "\n\t\t\t\t"); + ezxml_add_txt(abb, "\n\t\t\t"); + ezxml_add_txt(ab, "\n\t\t\t"); } return 1; } @@ -320,7 +360,7 @@ determine_angle_basis(const char *fn, ezxml_t wtl) if (wtl == NULL) return -1; ids = ezxml_txt(ezxml_child(wtl, "IncidentDataStructure")); - if (ids == NULL) + if (!ids[0]) return -1; for (i = 0; i < ABend; i++) { ezxml_t parsed = ezxml_parse_str(basis_definition[i], @@ -343,10 +383,55 @@ determine_angle_basis(const char *fn, ezxml_t wtl) return -1; } +/* Filter Klems angle basis, factoring out incident projected solid angle */ +static int +filter_klems_matrix(FILE *fp) +{ +#define MAX_COLUMNS 145 + const char *bn = klems_basis_name[angle_basis]; + float col_corr[MAX_COLUMNS]; + int i, j, n = nabases; + /* get angle basis */ + while (n-- > 0) + if (!strcasecmp(bn, abase_list[n].name)) + break; + if (n < 0) + return 0; + if (abase_list[n].nangles > MAX_COLUMNS) { + fputs("Internal error - too many Klems columns!\n", stderr); + return 0; + } + /* get correction factors */ + for (j = abase_list[n].nangles; j--; ) + col_corr[j] = 1.f / io_getohm(j, &abase_list[n]); + /* read/correct/write matrix */ + for (i = 0; i < abase_list[n].nangles; i++) { + for (j = 0; j < abase_list[n].nangles; j++) { + double d; + if (fscanf(fp, "%lf", &d) != 1) + return 0; + if (d < -1e-3) { + fputs("Negative BSDF data!\n", stderr); + return 0; + } + printf(" %.3e", d*col_corr[j]*(d > 0)); + } + fputc('\n', stdout); + } + while ((i = getc(fp)) != EOF) + if (!isspace(i)) { + fputs("Unexpected data past EOF\n", stderr); + return 0; + } + return 1; /* all is good */ +#undef MAX_COLUMNS +} + /* Write out BSDF data block with surrounding tags */ static int writeBSDFblock(const char *caller, struct s_dfile *df) { + int correct_klems = correct_solid_angle; char *cp; puts("\t"); @@ -354,7 +439,7 @@ writeBSDFblock(const char *caller, struct s_dfile *df) switch (df->spectrum) { case DSvisible: puts("\t\tVisible"); - puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp"); + puts("\t\tCIE Illuminant D65 1nm.ssp"); puts("\t\tASTM E308 1931 Y.dsp"); break; case DSxbar31: @@ -412,17 +497,19 @@ writeBSDFblock(const char *caller, struct s_dfile *df) puts(""); switch (angle_basis) { case ABklemsFull: - puts("\t\t\tLBNL/Klems Full"); - break; case ABklemsHalf: - puts("\t\t\tLBNL/Klems Half"); - break; case ABklemsQuarter: - puts("\t\t\tLBNL/Klems Quarter"); + fputs("\t\t\t", stdout); + fputs(klems_basis_name[angle_basis], stdout); + puts(""); + fputs("\t\t\t", stdout); + fputs(klems_basis_name[angle_basis], stdout); + puts(""); break; case ABtensorTree3: case ABtensorTree4: puts("\t\t\tLBNL/Shirley-Chiu"); + correct_klems = 0; break; default: fprintf(stderr, "%s: bad angle basis (%d)\n", caller, angle_basis); @@ -431,16 +518,39 @@ writeBSDFblock(const char *caller, struct s_dfile *df) puts("\t\t\tBTDF"); puts("\t\t\t"); fflush(stdout); - if (df->fname == stdin_name) { + if (correct_klems) { /* correct Klems matrix data */ + FILE *fp = stdin; + if (df->fname[0] == '!') + fp = popen(df->fname+1, "r"); + else if (df->fname != stdin_name) + fp = fopen(df->fname, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: cannot open '%s'\n", + caller, df->fname); + return 0; + } + if (!filter_klems_matrix(fp)) { + fprintf(stderr, "%s: Klems data error from '%s'\n", + caller, df->fname); + return 0; + } + if (df->fname[0] != '!') { + fclose(fp); + } else if (pclose(fp)) { + fprintf(stderr, "%s: error running '%s'\n", + caller, df->fname); + return 0; + } + } else if (df->fname == stdin_name) { copy_and_close(fileno(stdin)); - } else if (*df->fname != '!') { + } else if (df->fname[0] != '!') { if (!copy_and_close(open(df->fname, O_RDONLY))) { fprintf(stderr, "%s: error reading from '%s'\n", caller, df->fname); return 0; } } else if (system(df->fname+1)) { - fprintf(stderr, "%s: error running '%s'\n", caller, df->fname+1); + fprintf(stderr, "%s: error running '%s'\n", caller, df->fname); return 0; } puts("\t\t\t"); @@ -453,7 +563,7 @@ writeBSDFblock(const char *caller, struct s_dfile *df) static int writeBSDF(const char *caller, ezxml_t fl) { - char *xml = ezxml_toxml(fl); + char *xml = ezxml_toxml(fl); /* store XML in string */ int ei, i; /* locate trailer */ for (ei = strlen(xml)-strlen(""); @@ -477,7 +587,8 @@ writeBSDF(const char *caller, ezxml_t fl) return 0; } fputs(xml+ei, stdout); /* write trailer */ - free(xml); + free(xml); /* free string */ + fputc('\n', stdout); return (fflush(stdout) == 0); } @@ -485,22 +596,39 @@ writeBSDF(const char *caller, ezxml_t fl) static int wrapBSDF(const char *caller) { - const char *xml_path = getpath((char *)xml_input, getrlibpath(), R_OK); + const char *xml_path = xml_input; ezxml_t fl, wtl; /* load previous XML/template */ - if (xml_path == NULL) { - fprintf(stderr, "%s: cannot find XML file named '%s'\n", - caller, xml_input==NULL ? "NULL" : xml_input); - return 0; + if (xml_input == stdin_name) { + fl = ezxml_parse_fp(stdin); + } else if (xml_input[0] == '!') { + FILE *pfp = popen(xml_input+1, "r"); + if (pfp == NULL) { + fprintf(stderr, "%s: cannot start process '%s'\n", + caller, xml_input); + return 0; + } + fl = ezxml_parse_fp(pfp); + if (pclose(pfp)) { + fprintf(stderr, "%s: error running '%s'\n", + caller, xml_input); + return 0; + } + } else { + xml_path = getpath((char *)xml_input, getrlibpath(), R_OK); + if (xml_path == NULL) { + fprintf(stderr, "%s: cannot find XML file named '%s'\n", + caller, xml_input==NULL ? "NULL" : xml_input); + return 0; + } + fl = ezxml_parse_file(xml_path); } - fl = ezxml_parse_file(xml_path); if (fl == NULL) { - fprintf(stderr, "%s: cannot open XML path '%s'\n", - caller, xml_path); + fprintf(stderr, "%s: cannot load XML '%s'\n", caller, xml_path); return 0; } if (ezxml_error(fl)[0]) { - fprintf(stderr, "%s: error in XML %s: %s\n", caller, xml_path, + fprintf(stderr, "%s: error in XML '%s': %s\n", caller, xml_path, ezxml_error(fl)); goto failure; } @@ -522,18 +650,17 @@ wrapBSDF(const char *caller) goto failure; } /* make material assignments */ - if (!mat_assignments(caller, xml_path, wtl)) { + if (!mat_assignments(caller, xml_path, wtl)) goto failure; - } if (mgf_geometry != NULL) { /* add geometry if specified */ ezxml_t geom = ezxml_child(wtl, "Geometry"); if (geom == NULL) - geom = ezxml_add_child(wtl, "Geometry", 0); + geom = ezxml_add_child(wtl, "Geometry", strlen(wtl->txt)); ezxml_set_attr(geom, "format", "MGF"); geom = ezxml_child(geom, "MGFblock"); if (geom == NULL) { geom = ezxml_child(wtl, "Geometry"); - geom = ezxml_add_child(geom, "MGFblock", strlen(geom->txt)); + geom = ezxml_add_child(geom, "MGFblock", 0); } ezxml_set_attr(geom, "unit", attr_unit); ezxml_set_txt(geom, input2str(mgf_geometry)); @@ -542,20 +669,20 @@ wrapBSDF(const char *caller) } /* check basis */ if (angle_basis != ABdefault) { + size_t offset = 0; ezxml_t ab, dd = ezxml_child(wtl, "DataDefinition"); - if (dd == NULL) { - dd = ezxml_add_child(wtl, "DataDefinition", 0); - } else { - if (ezxml_child(dd, "DataDefinition") != NULL) + if (dd != NULL) { + offset = dd->off; + if (dd->child != NULL) fprintf(stderr, "%s: warning - replacing existing in '%s'\n", caller, xml_path); - while (dd->child != NULL) - ezxml_remove(dd->child); - } - ezxml_insert(ezxml_parse_str(basis_definition[angle_basis], + ezxml_remove(dd); + } else + offset = strlen(wtl->txt); + dd = ezxml_insert(ezxml_parse_str(basis_definition[angle_basis], strlen(basis_definition[angle_basis])), - dd, 0); + wtl, offset); if ((ab = ezxml_child(dd, "AngleBasis")) != NULL && !finish_angle_basis(ab)) goto failure; @@ -580,7 +707,9 @@ UsageExit(const char *pname) { fputs("Usage: ", stderr); fputs(pname, stderr); - fputs(" [options] [input.xml]\n", stderr); + fputs(" [-W][-a {kf|kh|kq|t3|t4}][-u unit][-g geom][-f 'x=string;y=string']", stderr); + fputs(" [-s spectr][-tb inp][-tf inp][-rb inp][-rf inp]", stderr); + fputs(" [input.xml]\n", stderr); exit(1); } @@ -617,8 +746,8 @@ main(int argc, char *argv[]) argv[0]); return 1; } - if (strstr(argv[i], legal_units) == NULL) { - fprintf(stderr, "%s: unit must be one of (%s)\n", + if (strstr(legal_units, argv[i]) == NULL) { + fprintf(stderr, "%s: -u unit must be one of (%s)\n", argv[0], legal_units); return 1; } @@ -644,6 +773,9 @@ main(int argc, char *argv[]) angle_basis = ABtensorTree4; else UsageExit(argv[0]); + continue; + case 'c': /* correct solid angle */ + correct_solid_angle ^= 1; continue; case 't': /* transmission */ if (i >= argc-1)