From a649fa3a79c00d131e7e25d349c01b9c0bd7fac5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 26 Apr 2022 06:20:00 -0400 Subject: [PATCH 01/11] detect yaml file output by file name --- src/fix_ave_time.cpp | 4 +++- src/fix_ave_time.h | 1 + unittest/utils/test_utils.cpp | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/fix_ave_time.cpp b/src/fix_ave_time.cpp index ee08c859f4..660d8c155d 100644 --- a/src/fix_ave_time.cpp +++ b/src/fix_ave_time.cpp @@ -1009,6 +1009,7 @@ void FixAveTime::options(int iarg, int narg, char **arg) noff = 0; offlist = nullptr; overwrite = 0; + yaml_flag = false; format_user = nullptr; format = (char *) " %g"; title1 = nullptr; @@ -1020,11 +1021,12 @@ void FixAveTime::options(int iarg, int narg, char **arg) while (iarg < narg) { if (strcmp(arg[iarg],"file") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix ave/time command"); + yaml_flag = utils::strmatch(arg[iarg+1],"\\.[yY][aA]?[mM][lL]$"); if (me == 0) { fp = fopen(arg[iarg+1],"w"); if (fp == nullptr) error->one(FLERR,"Cannot open fix ave/time file {}: {}", - arg[iarg+1], utils::getsyserror()); + arg[iarg+1], utils::getsyserror()); } iarg += 2; } else if (strcmp(arg[iarg],"ave") == 0) { diff --git a/src/fix_ave_time.h b/src/fix_ave_time.h index d2c7b4752b..dda548b646 100644 --- a/src/fix_ave_time.h +++ b/src/fix_ave_time.h @@ -48,6 +48,7 @@ class FixAveTime : public Fix { int any_variable_length; int all_variable_length; int lockforever; + bool yaml_flag; int ave, nwindow, startstep, mode; int noff, overwrite; diff --git a/unittest/utils/test_utils.cpp b/unittest/utils/test_utils.cpp index 900ce6814c..e452314419 100644 --- a/unittest/utils/test_utils.cpp +++ b/unittest/utils/test_utils.cpp @@ -519,6 +519,27 @@ TEST(Utils, strmatch_opt_char) ASSERT_FALSE(utils::strmatch("x_name", "^[cfvid]2?_name")); } +TEST(Utils, strmatch_yaml_suffix) +{ + ASSERT_TRUE(utils::strmatch("test.yaml", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_TRUE(utils::strmatch("test.yml", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_TRUE(utils::strmatch("TEST.YAML", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_TRUE(utils::strmatch("TEST.YML", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("test.yamlx", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("test.ymlx", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("TEST.YAMLX", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("TEST.YMLX", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("testyaml", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("testyml", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("TESTYAML", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("TESTYML", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("yaml.test", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("yml.test", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("YAML.TEST", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("YML.TEST", "\\.[yY][aA]?[mM][lL]$")); + ASSERT_FALSE(utils::strmatch("test", "\\.[yY][aA]?[mM][lL]$")); +} + TEST(Utils, strmatch_dot) { ASSERT_TRUE(utils::strmatch("rigid", ".igid")); From cfc4dcea3df63bccf5f9f34ededb427f5419e163 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 28 Apr 2022 07:34:23 -0400 Subject: [PATCH 02/11] must quote keyword data to avoid issues parsing the YAML data --- src/thermo.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/thermo.cpp b/src/thermo.cpp index 0a810662ca..257c2b78ce 100644 --- a/src/thermo.cpp +++ b/src/thermo.cpp @@ -335,10 +335,8 @@ void Thermo::header() hdr += fmt::format("{:^14} ", head); else if ((vtype[i] == INT) || (vtype[i] == BIGINT)) hdr += fmt::format("{:^11} ", head); - } else if (lineflag == YAMLLINE) { - hdr += keyword[i]; - hdr += ", "; - } + } else if (lineflag == YAMLLINE) + hdr += fmt::format("'{}', ", head); } if (lineflag == YAMLLINE) hdr += "]\ndata:"; From 09ed718c14c80e79ade572660cc2080ea0d9bb73 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 28 Apr 2022 07:35:08 -0400 Subject: [PATCH 03/11] add example to plot thermo data with pandas+matplotlib --- doc/src/Howto_structured_data.rst | 58 +++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/doc/src/Howto_structured_data.rst b/doc/src/Howto_structured_data.rst index b320e87279..df8e6de0f6 100644 --- a/doc/src/Howto_structured_data.rst +++ b/doc/src/Howto_structured_data.rst @@ -55,6 +55,9 @@ JSON YAML format thermo_style output =============================== +Extracting data from log file +----------------------------- + .. versionadded:: 24Mar2022 LAMMPS supports the thermo style "yaml" and for "custom" style @@ -66,7 +69,7 @@ the following style: .. code-block:: yaml --- - keywords: [Step, Temp, E_pair, E_mol, TotEng, Press, ] + keywords: ['Step', 'Temp', 'E_pair', 'E_mol', 'TotEng', 'Press', ] data: - [100, 0.757453103239935, -5.7585054860159, 0, -4.62236133677021, 0.207261053624721, ] - [110, 0.759322359337036, -5.7614668389562, 0, -4.62251889318624, 0.194314975399602, ] @@ -80,9 +83,9 @@ This data can be extracted and parsed from a log file using python with: import re, yaml try: - from yaml import CSafeLoader as Loader, CSafeDumper as Dumper + from yaml import CSafeLoader as Loader except ImportError: - from yaml import SafeLoader as Loader, SafeDumper as Dumper + from yaml import SafeLoader as Loader docs = "" with open("log.lammps") as f: @@ -109,6 +112,55 @@ of that run: Number of runs: 2 TotEng = -4.62140097780047 +Processing/plotting extracted data with Pandas +---------------------------------------------- + +.. figure:: JPG/thermo_bondeng.png + :figwidth: 33% + :align: right + +After extracting the YAML format data and parsing it, it can be easily +imported for further processing with the `pandas +`_ and `matplotlib +`_ Python modules. The JSON format parser in +*pandas* can also process data imported from YAML files. Because of the +organization of the data in the YAML format thermo output, it needs to +be told to process only the 'data' part of the imported data and +*normalize* it, and then one needs to set the column names from the +'keywords' entry later. The following example Python script code +demonstrates this, and creates the image shown on the right of a simple +plot of various bonded energy contributions versus the timestep from a +run of the 'peptide' example input after changing the :doc:`thermo style +` to 'yaml'. The properties to be used for x and y values +can be conveniently selected through the keywords. Please note that +those keywords can be changed to custom strings with the +:doc:`thermo_modify colname ` command. + +.. code-block:: python + + import re, yaml + import pandas as pd + import matplotlib.pyplot as plt + + try: + from yaml import CSafeLoader as Loader + except ImportError: + from yaml import SafeLoader as Loader + + docs = "" + with open("log.lammps") as f: + for line in f: + m = re.search(r"^(keywords:.*$|data:$|---$|\.\.\.$| - \[.*\]$)", line) + if m: docs += m.group(0) + '\n' + + thermo = list(yaml.load_all(docs, Loader=Loader)) + + df = pd.json_normalize(thermo[0],'data') + df.columns = thermo[0]['keywords'] + fig = df.plot(x='Step', y=['E_bond', 'E_angle', 'E_dihed', 'E_impro']) + plt.savefig('thermo_bondeng.png') + + Writing continuous data during a simulation =========================================== From 2f71c96bde9617178f508464e60093620a57413f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 28 Apr 2022 07:46:47 -0400 Subject: [PATCH 04/11] add yaml format output for mode scalar --- src/fix_ave_time.cpp | 66 ++++++++++++++++++++++++++++++++++++-------- src/fix_ave_time.h | 8 +++++- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/fix_ave_time.cpp b/src/fix_ave_time.cpp index 660d8c155d..481611a3cc 100644 --- a/src/fix_ave_time.cpp +++ b/src/fix_ave_time.cpp @@ -83,6 +83,8 @@ FixAveTime::FixAveTime(LAMMPS *lmp, int narg, char **arg) : int expand = 0; char **earg; nvalues = utils::expand_args(FLERR,nvalues,&arg[6],mode,earg,lmp); + keyword.resize(nvalues); + key2col.clear(); if (earg != &arg[6]) expand = 1; arg = earg; @@ -98,6 +100,8 @@ FixAveTime::FixAveTime(LAMMPS *lmp, int narg, char **arg) : for (int i = 0; i < nvalues; i++) { ArgInfo argi(arg[i]); + keyword[i] = arg[i]; + key2col[arg[i]] = i; if ((argi.get_type() == ArgInfo::NONE) || (argi.get_type() == ArgInfo::UNKNOWN) @@ -269,9 +273,8 @@ FixAveTime::FixAveTime(LAMMPS *lmp, int narg, char **arg) : for (int i = 0; i < nvalues; i++) fprintf(fp," %s",earg[i]); fprintf(fp,"\n"); } - if (ferror(fp)) - error->one(FLERR,"Error writing file header"); - + if (yaml_flag) fputs("---\n",fp); + if (ferror(fp)) error->one(FLERR,"Error writing file header: {}", utils::getsyserror()); filepos = platform::ftell(fp); } @@ -456,8 +459,10 @@ FixAveTime::~FixAveTime() delete[] extlist; - if (fp && me == 0) fclose(fp); - + if (fp && me == 0) { + if (yaml_flag) fputs("...\n", fp); + fclose(fp); + } memory->destroy(column); delete[] vector; @@ -669,11 +674,22 @@ void FixAveTime::invoke_scalar(bigint ntimestep) if (fp && me == 0) { clearerr(fp); if (overwrite) platform::fseek(fp,filepos); - fmt::print(fp,"{}",ntimestep); - for (i = 0; i < nvalues; i++) fprintf(fp,format,vector_total[i]/norm); - fprintf(fp,"\n"); - if (ferror(fp)) error->one(FLERR,"Error writing out time averaged data"); - + if (yaml_flag) { + if (!yaml_header || overwrite) { + yaml_header = true; + fputs("keywords: ['Step', ", fp); + for (auto k : keyword) fmt::print(fp, "'{}', ", k); + fputs("]\ndata:\n", fp); + } + fmt::print(fp, " - [{}, ", ntimestep); + for (i = 0; i < nvalues; i++) fmt::print(fp,"{}, ",vector_total[i]/norm); + fputs("]\n", fp); + } else { + fmt::print(fp,"{}",ntimestep); + for (i = 0; i < nvalues; i++) fprintf(fp,format,vector_total[i]/norm); + fprintf(fp,"\n"); + if (ferror(fp)) error->one(FLERR,"Error writing out time averaged data"); + } fflush(fp); if (overwrite) { @@ -994,6 +1010,34 @@ double FixAveTime::compute_array(int i, int j) return 0.0; } +/* ---------------------------------------------------------------------- + modify settings +------------------------------------------------------------------------- */ + +int FixAveTime::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0], "colname") == 0) { + if (narg < 3) utils::missing_cmd_args(FLERR, "fix_modify colname", error); + int icol = -1; + if (utils::is_integer(arg[1])) { + icol = utils::inumeric(FLERR, arg[1], false, lmp); + if (icol < 0) icol = keyword.size() + icol + 1; + icol--; + } else { + try { + icol = key2col.at(arg[1]); + } catch (std::out_of_range &) { + icol = -1; + } + } + if ((icol < 0) || (icol >= (int) keyword.size())) + error->all(FLERR, "Thermo_modify colname column {} invalid", arg[1]); + keyword[icol] = arg[2]; + return 3; + } + return 0; +} + /* ---------------------------------------------------------------------- parse optional args ------------------------------------------------------------------------- */ @@ -1009,7 +1053,7 @@ void FixAveTime::options(int iarg, int narg, char **arg) noff = 0; offlist = nullptr; overwrite = 0; - yaml_flag = false; + yaml_flag = yaml_header = false; format_user = nullptr; format = (char *) " %g"; title1 = nullptr; diff --git a/src/fix_ave_time.h b/src/fix_ave_time.h index dda548b646..55df82c1f7 100644 --- a/src/fix_ave_time.h +++ b/src/fix_ave_time.h @@ -22,6 +22,8 @@ FixStyle(ave/time,FixAveTime); #include "fix.h" +#include + namespace LAMMPS_NS { class FixAveTime : public Fix { @@ -32,6 +34,7 @@ class FixAveTime : public Fix { void init() override; void setup(int) override; void end_of_step() override; + int modify_param(int, char **) override; double compute_scalar() override; double compute_vector(int) override; double compute_array(int, int) override; @@ -48,7 +51,7 @@ class FixAveTime : public Fix { int any_variable_length; int all_variable_length; int lockforever; - bool yaml_flag; + bool yaml_flag, yaml_header; int ave, nwindow, startstep, mode; int noff, overwrite; @@ -57,6 +60,9 @@ class FixAveTime : public Fix { char *title1, *title2, *title3; bigint filepos; + std::map key2col; + std::vector keyword; + int norm, iwindow, window_limit; double *vector; double *vector_total; From 04b46a9ce8955000df9a5ee3e033f24dc77db729 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 28 Apr 2022 14:33:15 -0400 Subject: [PATCH 05/11] implement yaml output for mode vector --- src/fix_ave_time.cpp | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/fix_ave_time.cpp b/src/fix_ave_time.cpp index 481611a3cc..51e201000c 100644 --- a/src/fix_ave_time.cpp +++ b/src/fix_ave_time.cpp @@ -896,11 +896,34 @@ void FixAveTime::invoke_vector(bigint ntimestep) if (fp && me == 0) { if (overwrite) platform::fseek(fp,filepos); - fmt::print(fp,"{} {}\n",ntimestep,nrows); - for (i = 0; i < nrows; i++) { - fprintf(fp,"%d",i+1); - for (j = 0; j < nvalues; j++) fprintf(fp,format,array_total[i][j]/norm); - fprintf(fp,"\n"); + if (yaml_flag) { + if (!yaml_header || overwrite) { + yaml_header = true; + fputs("keywords:\n - Step\n", fp); + bool first = true; + for (auto k : keyword) { + if (first) { + first = false; + fmt::print(fp, " - - '{}'\n", k); + } else fmt::print(fp, " - '{}'\n", k); + } + fputs("data:\n", fp); + } + fmt::print(fp, " {}:\n", ntimestep); + for (i = 0; i < nvalues; i++) { + for (j = 0; j < nrows; j++) { + if (j == 0) fputs(" - - ", fp); + else fputs(" - ", fp); + fmt::print(fp,"{}\n",array_total[j][i]/norm); + } + } + } else { + fmt::print(fp,"{} {}\n",ntimestep,nrows); + for (i = 0; i < nrows; i++) { + fprintf(fp,"%d",i+1); + for (j = 0; j < nvalues; j++) fprintf(fp,format,array_total[i][j]/norm); + fprintf(fp,"\n"); + } } fflush(fp); if (overwrite) { From f25b60fded7871b0d45a9b037577beb8792e9d2a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 28 Apr 2022 16:02:45 -0400 Subject: [PATCH 06/11] simplify pandas processing. add missing image with plot --- doc/src/Howto_structured_data.rst | 30 ++++++++++++++---------------- doc/src/JPG/thermo_bondeng.png | Bin 0 -> 32598 bytes 2 files changed, 14 insertions(+), 16 deletions(-) create mode 100644 doc/src/JPG/thermo_bondeng.png diff --git a/doc/src/Howto_structured_data.rst b/doc/src/Howto_structured_data.rst index df8e6de0f6..6b45f945c5 100644 --- a/doc/src/Howto_structured_data.rst +++ b/doc/src/Howto_structured_data.rst @@ -122,19 +122,18 @@ Processing/plotting extracted data with Pandas After extracting the YAML format data and parsing it, it can be easily imported for further processing with the `pandas `_ and `matplotlib -`_ Python modules. The JSON format parser in -*pandas* can also process data imported from YAML files. Because of the -organization of the data in the YAML format thermo output, it needs to -be told to process only the 'data' part of the imported data and -*normalize* it, and then one needs to set the column names from the -'keywords' entry later. The following example Python script code -demonstrates this, and creates the image shown on the right of a simple -plot of various bonded energy contributions versus the timestep from a -run of the 'peptide' example input after changing the :doc:`thermo style -` to 'yaml'. The properties to be used for x and y values -can be conveniently selected through the keywords. Please note that -those keywords can be changed to custom strings with the -:doc:`thermo_modify colname ` command. +`_ Python modules. Because of the organization +of the data in the YAML format thermo output, it needs to be told to +process only the 'data' part of the imported data to create a pandas +dataframe, and one needs to set the column names from the 'keywords' +entry. The following Python script code example demonstrates this, and +creates the image shown on the right of a simple plot of various bonded +energy contributions versus the timestep from a run of the 'peptide' +example input after changing the :doc:`thermo style ` to +'yaml'. The properties to be used for x and y values can be +conveniently selected through the keywords. Please note that those +keywords can be changed to custom strings with the :doc:`thermo_modify +colname ` command. .. code-block:: python @@ -155,9 +154,8 @@ those keywords can be changed to custom strings with the thermo = list(yaml.load_all(docs, Loader=Loader)) - df = pd.json_normalize(thermo[0],'data') - df.columns = thermo[0]['keywords'] - fig = df.plot(x='Step', y=['E_bond', 'E_angle', 'E_dihed', 'E_impro']) + df = pd.DataFrame(data=thermo[0]['data'], columns=thermo[0]['keywords']) + fig = df.plot(x='Step', y=['E_bond', 'E_angle', 'E_dihed', 'E_impro'], ylabel='Energy in kcal/mol') plt.savefig('thermo_bondeng.png') diff --git a/doc/src/JPG/thermo_bondeng.png b/doc/src/JPG/thermo_bondeng.png new file mode 100644 index 0000000000000000000000000000000000000000..28ac9d26cc4c66c10e7e562bdca701fc2841175a GIT binary patch literal 32598 zcma&ObyQYu*Ds2MbazNgOG~$uv`9%xqjaZW&>*373WA7~bc501G}IKZF(@z)5D>5*E6QpjARr1NAl&(eh6q3L(%(OW z{~%jSt4bpvltyD-nxeqJW!>cT+@3mGxp|tpSR&}@(0W+9xV~_7pylJ_;WmBn(%s(7 zQT~M&yPSg!t)mq!FE_Uk4=;}ZEz1iBH%pfn_H?Z5@+$VGuC@;*bSn@LXb~REN^5zg zZO%N?GSE#CylnpWOorA9&n)4mid6+q2VYpaP*~|wWc)X!M#op9qqaxusZDE@=h@>u zswF$w&Fc+oJ|p>Bxr>o%xoO{os23-gUjHPUdW{-L{@{nz-S>*9*mLbarRU`anE_2A16054n_B zWzgt;M*J@yoxXASvE(e0?ogdNfLm%sD1+wxx_5bT&x=0RW^F`fZE1GREkoG#OH)(E z?e}ETdzaa`xaR6TcZQ{jl^FaTs_UDYnu?2~#(HT3V`Bcf)ULy^u`%OtGHe~JnHM;) za&UH8WKey*(R{s8mM#moAl)^zYobTjY#_IH#@PyLz{83{0g-Z;R0#xSoeO{=oJ zKgE|pd*=B6aqItoKVm-|+hf>BCH-<|f;mg+HNV*4d%D2Wd$~mDtc2n=S5%J#W2-2i zq0){Z*GUKB-N2e#Ugq!7bQ=h4Hz;eqJVWpPNzSW@Ppf>I zQb5}yrPyQmIS`R0V0cBwu%$Q^G|dn?i}s9&jNQl9=?oXICq@qghih<)F|be!&UeV< zGqf*!zK)+7@m3Ho9tDTg`HbcIJ{e}@dpuU!I65ZMdE~mHz!+kAR=KyS_C$iwt-JT) zoLRh(!gA;L@4W6+Fk=m$>xjHJvqGnteK<(I8pDi{7xRkJh^i2u zIOGeBuh}D=KE)WzlB$TuTjYw=Bwp=o~T*HW?m43rnuz?sIk~iHP-B zHsQg4_f{}Q@A&UiB}8;h&rho>F$#-gf;Jb3|9%qNv;X(^Xt{NH?%=rKNXoajnaL_y zh{1P$kbd4$p1x$a`^^4ZN2co=gzTHD?`CGL2VFk+!|@YubiYZDMRbWHlD@N57}~o> z^@X^GgU-So*7}FO22X4mws(nCkwK{~`%=sWZ<$HpD=J-y><5JC=yI@ipO`KN&xOxl z;+Ny2I2QVe8+-9$7%RvV!;dfN?2{E0yRwwr2!yJ5rZ4YqG1Ep%Uyr~_(`iPxWVXJL zQ^)4Kxr0bfp!_rU_Jr7QLMbxJx?hXuJP2LFkrgv|J(v4ILWXVsKCjvf*>fAe-F<|X z6WWgyQl-Mkh&&idgN_`HdG+2#7-IuZKrCdFl+GY5JgnX6Op!wxR`Sp5jDwnU{0@hU zy`{;^!PTUZnTtqzfVDX?*Q5VKEJI$ZH z7%RHj=epUCzB!F{sJ>_mBRtujF$g-2kt&N`7;~<;xwp?k)PPeIz{W(vS8us!i$GeG z`T1(+yL#cQE_0w*)2JfryPvyD(UMHe%!!GK2WNf=I3Y`8MFvW_E^w;Hj~};hJv|+_VQk~5&X6eA$~j)VkXm!jvuzYojObk=lTOvuA)8}9G7!^*xs+~mjdV^qpNwwdi$)i* z>uB=Q&aSYks;aV5n}uj>Y^*cu6f;2?M*noCUxCG^sOg=?ciFdSWvs#oN3yVtFWY}``OXV+P-pt ziHUhOuPESM)*MT}bg^U&e(F%B+nNJxlp_N`~xrLw9jL)=$~Ej}tTlKxQ8CUeL1fYj!3Qh8S8))HEIbOPc9( zQCT*kCKPR%PpNV;N$1C7loWSDUlm3mN!y}Z-`{bku}j6%Wv8VhyJc2>4@g;9~`V?thQy9uHtejp9uq={)~ZkAlq^vKdO5ai9%FAPG05R6|zbXRLW4_ZCxW>pDUHoCbcoy5*Y9}Z#c%t-F*TXL{!=(j1@v| zZ?f22Z1&2`uMJ(eoU2`Vc36IWtFYFrl_hF4sH`%=~2sOuJgo9~36 z1Un90(#i7XY$B)=ty;y-Th)flV%pr0bSUE~#a@$pEUkL-<|kMuxaaPo@@=6~u=!t+OO zx%8QGks~~N8BJs43tNWG;xsMfRLO^qm+jiyz6j!fq--E2j9A!@)p(m-{c~dQDN+^z zB5U^H8wV0}ijj}2j&B!`eSWnrOSz!H`g%{XZ6UTUOS8PwsM7l2J>w)y9jjTco`@zO z`h}S^FcZlb*Q-S#Ps(-lr79uM8DI`>GV9#gXLxMhc zf?+sxbaE>N9sn}%O7(=D3CC?)cf7P51(#b*-&ww671fOLEi5Ky-^q#Yfy8{vt9XXy9Ymm zVD)Yh-ZP>y+!oW0N_D3r^U%17-Ai6eeTxTsK0Kq*+pgSx5%s!s*b3L|gF6b-Uk0h! z&{*R%|K{IrB#IAbpc5pcR#=xWiW;B8C9&=`axCJDdb$|ox@T=SX;xT<(iiA%Hwby; zVYw5#RGMsAktRCT^?6N1Pm_iR$3m!r4 ze4x+rdOz^E&;ZC(Em~{!>J>f$@3G7c)rTHBkRz5GM7c!Xn$&{lTuXnRB%zM0-NwIR z8}X-8`@V_i4QQoQGA}erGLa123=L478MILmxYyRkJXYdS3Rw?e%QFgE?-S!>nKk38YKb#P+RPlOBODQNb z9?8(j_DEIRH-3(zS8w|ryl-7WvzZlZUbu@aP}}uP3(dox>OOI|*55 z%crOG7WZCX3o|B2W#hQ@c6}~tj@$P^5HpMBEh{_=4^siYIMHwFljq0ibwWP&bIizYf6zr@wfTzm4Dm6 z5h43s18dfYV{h04+GM#t2t2AL4$F+)jJrZlN_4UQk{m#z>TPYR@fj zc#RfIg@~SAhB(7f_PRCjO)qzv)QK>+j*TFQJI$&`YHz=N* zkToBAV)YQU5sN&^SxPA1B|2}JC!NPuMgdW1C8W=%Qe)@5CUwYs3w6#!I-P3O3g1Jx zNo`sJyVa9y>$M2Fw?fPwM|PRbU7kQV_pn9ws(stvtP&j0b>}6n)q>0&-6uNY!#1MD zuOfdIDE@hSsf%BFdvb;tdUxhDEBTu(yJ1>PHCW?-$j?0nMvP=INK7*-b>} z^v4Xl_|aLV=Mo9UbV@bTpvx)F0^d3`YU^LB8F`(B_wYWHZAbE;Rq;jOTdk^1`k#V91J7zC2!sF!AJbBhN9evmss+eoCrBN}f z8Q%Uz>~8V}z6q8Q%J%Q95^BNdrC)N44#5uW6>-`K+qx>Cst6up7{7C1_iB--ctD?N13&D&=R4tn?Q6PdQUDo)pT%c&D2+^Y(HaFP3EWZcyA;Y|!kt&dG~j+2(TfKF-eZS9hpMhdpkqD4PxX{6q5@ zzs+++lCVg&Hd1=;jZMG!1>{KV!33FV;y?Wx^aUzNHlhLsJf&-@ZNS#dQ>J)hOIy6~ z>B*5l9F^5}zjWavvsdV3bxUipz~x`-nhuF%v*G=b`)lJ*>6oNbW~9nH*xxyRNfXOc zhbohJEu1Z@ukN-21>gfjJ)#Qs`w}7T^IKewkd8YnNN<{{?0cBv>_~9fX zd6#Ikw>`Z@??RhXN$#}QiecF%v?`RVRzXeVqT7`%w>22d4ZGQ^2^ujjZRXlF{=%n&M zFKPg1M(4I%$7{6Rt+Q-vX33=v;it+M#ZQ!+Dp@a3SUFj34r~%(w3i#U3S3N$z?()UP;rnwF{7+JbMPXg7F20j_EEXz$ji1H#JAcUv%NTjUZ;N;h zC2`g;3f3w7R6$9T(W_Rw=J0z+Sn-7^B}qvgUoz>Ip6UPYS@!d*eNM1~lf}I52N;-Esrt>VsJNWMNNcX34rl$Tid9sI+cWPCuGD3Rma}T2fKc zDcQ$YT|U&51ux>}O#I6%%@|T712uwcRJ9V0(TurNu2zL{ThX!jAta|0ft|w#Zgn_M zl!}lalYOYB6Y%tJ`gS39&XrYy_Shinfr9MIm~CsmKW}U^pYLK+Sat>6!(V&jFqm@u z@d2Ujj$?Aty;Um|@;sDxx~8WjM%D}5bXjQUD=~FQ-;nDb_`J%F(-&UkUag{Ia)cAt z-|gsCe14(59IA*}T*!;NcwUM^Ojto&chm2fdl>WZJ6;{tFoO*Ecwwxb34jCYopuVUtT@YrBe?&sQnh=Zd zIc`51mNy;FThRW)@^Ex^M>iLhx4;nR&{%52`uV074?cn!F>hwj2*{34TgstCUE*c_ zl2J2ik>7jw(%UR(==pZIIuq4K?<2=)Uf_6(#+ zq*<2(=TU$P{F6`5N0*`v_Eip7RYgn`C_(^y~5hg|Oj z6V{C1_2$Qz4fLlma~Xf4NSFz`RXkbvM+hpTU7Hl&;?98X3*M_FZ5%?8rq=!?JXnjV zx97&i?({B57y);7sp8cEGlDy0Le@iUN z2lgr?k64M(=!FTC@zNhQhr9|Pyqh{?5Rp>^^^Dhb`R1#e@X!T=z9g!7D`c804fYi( znCGQuEa&&rbjY}*Pw9{~+s<8A9t&6*N-+0)#+7)Xoi<5{dU)%pMl~k!{0; zVg^pG);v@}7HO@qOXG;EL>?#0Djbb!;@ETI-ftjJCC#%@&#z!d4gXcCq?LYN5tGG; zyOs>?Gd%Q*zSw)Tob^sUP1CN=>1-bTC*f;m1ohLA0L0M0(L!B>uLExKHG;olhjIln z`Vlt|F)}_;LP^nx3N(|Q+0mWJZ+*^pw`97x`5P5nZ9>#~Q$Szh=MPK91R!UB_ytyX z+ZAU8>MhMWJ?Hm76(EGTN>V4G4DCAwidhYdJ?q=0@!qHBGTEd})ik`*A51U@XGhJ# zLYD(0_3wrG>vy_5Uc`-+Kn@((vjxPwb&_`$&voHZHo^BxCow#4uGxEdzD}yjT4G(B zgfGMBy?gq!qpv0LX-AvPJlH`5sIJ3grE z+iX&4sN*eO<4Zg)(cy)AcoFN0)>AY3QrsJZ;Gw;+dKZ;a{=7h{whv>u`D=HwhPS=| zgO4dwQ;V*N2mZS zv|l^;8E;h`=`+}v(CFyGG}+v~KO;^K%PA_q`u4^OtFnJUSI#lb=ug`1=e8)hT3;6; z@INA{ihSsC&ZG96D0~T(CR{P3H1Kf4@1Hu$&EZp|m9PvPh?T@+HRl~ucPp^DgU3}|!~?Sr9s z(0iMQMhhApqt_E9=)R#F$7u%Q$-E`aCHjASbk<04wLMec-`_7TE{+rOi0PxHr6n;D zQD8_2((1;Bt&Pp<&Q78tV^C1g+1Z(9{_nXhI%q=5TxMu4EiHlb4E{3}&dRTJ(L^p2 zYHDi#y>`;h&Q8gT&GLiw>I;f~!y(*ygCl zQ{&^F+tanPv&IOVi3P>Q4j0E;z5a60HMMQ!Wz~k2(bqqZGilv8`m=Q$8ygGuF@m2& zCYbo9unu5b517($V;#hqMBvGE>F<+$wuZ-ty@D%w)ElwITUl9=5EGZCXXSQVlv!tZ zTS40aHbfzs`f4GBrlqANP=+p5dsBGxUF~4X=aIEppN*ng{N`KR`{XWkhtb`ezWo02 z;|Ex@?t6=J)X~g%XmCynN=i{tQQUhX6?TnCA+51WU>)vHTUJq7k&y= zN5`GrT_-QEn(>W$)YQj5jd1_C%^nM~M5+*OU3H5aA=kOEB}R2xT3Qw6ojCDImb}To zG9?4nhI7Fd&VBl=x_aD#x4old|LoHDWZS6TLzNbfk%#@=4s_qb4t?H)P#i|LW_5 zmOw27PKY_I!DOn_$4|>sPyS~X;N!=Gg99-6#i`YmlzM`(DfjpHf4{8MFY-Sc)pZw1 zAF@d^@O|sBb{|3}ri2x`SMJL_@wgJF&!?*FH$&&5C9iGDCn07N{7vEXR#yKQZj|#z z`j7fHD(=|_6h<@e9DKhRGOu@YudNzHD)8kN6Qc_Y@_VvPNfaq>h_fyRu z9@ea%xo*Xk{QLKB*YbXC)9;q8&xpU4_P%cn6PSF*e2ZL14EIiC@{~0 z1evMQgL=%x_2*|D4r}v(( zD)M0QJ$$(ABy{R0;f~|C;!;b6gWAfyDbM!C0f#P7>}?6_=F13c7d2##colB+qMLGqe4%JT~e&2p9VI?LZNr;L0q*L(jLADq*ZY#H+ zgb8Qjh{D#=hu&p7&z_|{jUR+EZ(+A{M_+Q8G+nOcWhNvfun~pVdgq_s9$w849lh<^ zk%iqoqr@}(Jwe&c2I>xRCnVRFpMjJaj1ATp$qb2_L3Qpxyj1P%dg@z-@kv4j*bps5EE17r-Y4* zn6Z(Zo?>zl4|{#;r*WL0iyWd-f9!eL6LK)e8biQH^B!{^VQn-e@dJVTR{lMv`r zQ&Z3k{PTw`UJ1|&69dD=#U)N@nT>y(l)7Ym>8SYB5(4GKzrTw>h$;#q+S#=$Dk=(- zl=G|Bx9;w4C|{~7DpVqFiX0^4XDghE%DI}A&|Y+NYdHIt3fH;&Zs8rSga%vu z+S-~of ztc*9JeHiW(?ss%_bYujHmWhc;QL|_1$as_5aWh$Ok3BIr(DCtmJ%O(sot@D%lE3^7 z5Ns;{golUcdRG`y# zQj;{v00{;Lq}QV_Rh5){&XyAaQs9#%h+Jp`R_b3t#$w^%TyI)SvU74a`CpyGL!_&% z^!TGf`8^Wv-qyefq6bJ86dYf0!otGR<4B+lV?m%UsV=o%oN*PuuJL-NbXvv=Jytt` zskSydm2ac#_y6S%4-d1kvB8}p*v-@(6_hlsZ*ErTalqvbs_Y=<7F=p!5UI&)Z*gm? zT9t_qAzlfpUtD4$>T>{|@e&gna&mGSnw|A^B~{g#TGu?8EHq5af}$dOTU&!tPc<~U zJ7p?Lqp(lo<3K08Z;!KZfwg=m;k2?irvSsr#85;$eEcWc+DF66T&3zLh;L-+pKV&? zY6_F?LWt1fO>6;Z{91O2SGq5{kaycJ9UUBunQ=hgY zzAttHgyivUKX!f3tIvwBo;ncdNwUhOGXfP(NcksnBS76SSu=su2P6?PSxFfg zReBs+28w9Ukbvp{_~g2>h=};eO_d}1MG{Kx`82BKjl8TZQx~R+(qJ*P+iQi^E(}jV z5ejL2qN~dst2jI|@{mH+&FyfxH!(FOWx_lE`uh4dG@oE+4cWk_l$4YMnUW@khJLW% z1-d0Jh$pHr2Nn;@T8iS28SN!T4FsvtM7DEj?{c(akY7UL`e@u_uEG1`D_Dq=&B^kK zPhVA-Hnz9@Pi8!snV9;l4uI3asoTx=$_FGa&Q>y;PiB3gytuh(m5lF9l$wXR``;(% z1_HSQ1aC$1=Irw9z)xas%}w(?4V?w{uKRk+(w>`#N7=?U7rL-4S1i& zH*jer8l1M!!(_|!>a=upzU1Wr%PTG}UR||;BBP_zfwK{xcuNP4{qt2*1xinpOUEVM zR%47ytV?1N58i?80o)@@F3q<6^I^6Pc)(x_VPK5Limj5nhlf855!`*3m6zAK z-~>t6)Fcrm+d`6xpI}^)84+Wtt)s(vY{Hvt*PHMF;`rJ5IqbjdQmIVV_x~KbJgF)$ zB-alJ`|{=b*4C?qAkvi7)FPb?+mc2oPGh5^%jU^H>K^j)Y67nu8YRDqju{-I}YoD#);c%{!xjg!lES9EU10X5Zrn zoDcgis{>`VGLY$LZy(=nF)}iO)oM3Y^*-UuTS`{8t?AXpw}OJnJVVaJnSxzYBqSuD z_l=E>zVAjHZr%p05 zEfDvRJ1`*S;o$)RjJM5&Qi5tc&|{L&iei2CLJTK_fgsGU_M<0YTgQ9KO^+aZdwZae z{vf!He69uu2NO;FU+^aP^z>|c_&Pc|DkyYLIX0&!CR+1T+^Rv_@nQ&*_fb)^wXW7K zF8jL8=c7osr0g1?vfYLs>POzla5n*49ijU;YnBh771EDtdu7 zeMo`*n#TWh;dE~ac+lpD6^CjA79xkKst4@s8xX)IetUARuCB($#t?R+1-c*<;mU7d zzoBfER9Bz8=@p@h;@1X;>5U@#9Az=ZJHtK)U51bd8K= zp#H*$6o}AzQ%z6L6Xs064=E`WZmgraAv8B8&4z~6?vru*USa~N_GV^pUwzXlAWcmK zehEQybaVvgo^AAnc!0p5jz&cYY3=UqW#{4|jc6AZ7M}CJ*y`#Eo!Wwg`Os|vlz#kH zkyPd6Sa9O3M8aA(c$eqptt_@jKvdn1()C%nIy!QJ1HkzAG({8f&HQk#vRUbA5UH_7 zKHRMlD91)9uV(dwcds(EQ2JIhm%mGFfije6cMGBU_2Rz?(39B95bFeJ$kfr+pe;cS z{6`{`t0=+r*=?F*Mss5k5)zKg%>@9*MnJ`-&WTI;e?suSlLE$TL(aM8fB-KqFGt4` z6{g;aZNX0gk{etc2M10$h+CouA2svy^Kbov^FU&Ib(@imBRkewF0g`d zXe2Ioodt6LWt6eDOIVS~)8av;Rn6BrJ~GT$DOqp6^P zX}_x|EiW%W5Pv30zv5&d8d)f`Yw(0gzrbH8pAJH(G2)Ssn0=!K}FblCNLC z!oc53W`O^KG-u+4MP)2~EUXqBc0u|IAa6*x)(Wgt`t> zpp@j~res#z&owoN84@+YdXUM;C}DNqnQeTUr)JdP1uE%*Ve<;OVgK>|F57^G*x6kk zPdWZqZOPIP87e)I1u);EF?sr}zTV*T(B*;T4WB*T4l_h9q!uVhm)eFeUpkgufOXUv z7DM%_H8g(qtb0Y$G{1K`eiDTPNJ`-8`OiF^0+6am${n$&fQKn5DM9Rv`J?Lq3DeWl zmL>-Oqd}Klgj=~GZ;VSkA?{Kgo5I4vSh=_YQsT@?RKT%B7jE+3eNp)b=#0ALRYhT; zN|r|7`_uN$&XEo|*Zq$ z8`mITAgxw*^x%e`=GP+Xyw zZ#svx@(3<=QiC%9tFg7UWyo0#yY}hR>qb^DKfg=JZ$Zadw^~CO$Gw05KKx0AV_nJ) zg{;dvvgtQ_NyGl`9_^;0cLR0wc==-DvP+F2O;r(?E1;$mMZHr~Qzg$=86w&VXlNt> z_&^{B1|r`M`W~*0;Dmhs{yj##yr>957)U9g(+WHcLY3VlFBp#}%{Pr%S<>{ePPNm= z$Hy>~3kduyZ{&f8B^Z0cfIfiZFJ9!Eq?XYGWR7S&J#ab*H>qW*`^xymV)7FOT+;v2 zOI;oJLaSg@M@vi05FH-d8>WCG+V^jZDIZ@s3^O-1`G@tAfE5xSZ&d5D4E)Q{&W?kf z-4@PWQZmrfBLXX`!6N4RuWxN658y{B*QfAy3AhdXNoAuuKc=MI)XRVpaO=Yg$?wa1?oY z`G5cZ$;-(Bkq}A$bxU)a{ed98+V5sY2o4DW$6*FiV{UHeBK|)nNi!yWD>v*5u470S zMwJm>EtSI)w^+&IO=a0Tp`oE)zkTyr%T1if11OBe?EPaWP(4+2>pb7PmCIxUpKf0lHpRicsMQ;7`8FgYi?EjUI4B{wBy9zg4hlpiI`}?I3YPdV^CbF2aBGUeEG=TwEMfgrKQW zWyTN1#hh~`x6;Ak%m{CVI)c>S@4&*8Vl!b5K z*dP*F|9d*km-p9>&RaupxeR?}=wmq&2!Zgyxz`21_bTR#Q(eDKoRV?Rpif_41tUtFQgw(DwCd+fF+ zpm5;-%gD$8frVEkbP9~>QagJ3`_m2Z4H*gUGcl>bdDYb+yntn~#`l2wXTEfmka73~?gm6Fm}Ee*Z;dA4sgjZb5NQ?<52U7nDaZ&K8t`Mon#5byB9Q<@GLO z6s-|LYp6HH9{`NHdtChzGSePx^T{$xusq=^6Y`LhtKz%}f$HDM%*r_+W|`5;h&@ zf;|g%_zCt0&= zYHDC@+pSDsHEvEjB|SVnX~ev~fBg!MtuPf1P;`=r^0G3pS|PvLAohboKlbm-C~%~ZvMI5%>Kh%0moTGP0behp=lKYp!a)Tpr?(J zR$}1+1NI_JIK=2lA|^MWx6}lh4S>Uj|5uSX{&4jn#s5lFYV|LJ{__S6a1&xmFx$vE zqiv^o94g2z`me4M{6CM{RFLiH@Ax!ikiAf85X%@?HX!Q_eU;<-ZKN;B$RRP1%bPYg zuH`JC+>}X+El3I7gy6^h@^}5_)1$u72}H$#c0<0_w~;l-5I2z-1{1w!w@2-SrMt+C z85fouxXECOfm_JO_fedlAdJM|-M6x`;f`&Nq&B*9tT@|&vR~It`G;HlcCf=xkN*L z3t&DyZRin}54a7lh?Y-I&&`z#+4S`G0&EFU;TTM0Yp?*sgGvQm4Q)0M;06W;>1x(c z(O{L7lrZE->7s2r`1l0Gt@tV5lbAQVd#LPWY*D|~duCU~B>E&aiADW4TSZup?0Z>; zzY?eHJ3-@X9=~#lE`I#wPqyx}>Jak{YG z4Z#HU1vu7+*w|9bUJ`*^PU8j}ZZdFO!Pxk({Q8~~*NGHuz0B3S+!A7m1&%AU4wlts zff~R28IzvgzjC0SDe-D=u{}H-1DGE$lHuVG$;rZee9#ZCtG_+qbv7NHgrCn(=ilLi zI@Z_!1J)7nZ4;BZfq?4+U;F0 zO2>xMwh4G0FpAU|)115lhcnH7z4|bfyj&?~n5{xMB~4Z?TPJIjtW&t@f0{2jTPFfG@jp-6n5m4B7mRPjqg&wS{Pyd#{TcgEU}o7? zX{PCw0e$Ru1G(=C^b8E3$H{}efjB`Y7NUBoT3!Mtx^vd6d)Cdnor~iEYeGUi=l|J> zu`B`na&>Wmx9vuM|Gp&{{?MBMH%QymbRn6~x(S*p;1z)zcZWM!1qiG{6jfM6#0B7^ zTAI)k6O$6>Ejh z<#uSh8960oL{wCQQZ5kuEsu-5py=+UQb8l?i>nKEe^YGQ2du^dxnk7O{Z^?4#yY?O zY6QcLOTa;Qg*Owyd$X{}(J$g;XTNm`fLgygKkVv4^G%V-VtMf30UH}M65^-WH`X`6 zXYf$j`p&DP9mrPCEvlW_b7w_!DK|DO*7{idV@xdVzldswIiJ#n+h$6-8nbUR1zq5B z(0E@GDhHBevVGa1fpy`_Sp9-Q5?PWyk5r;RLf&aAvTJuEzFz0D`u?>b@x;W3Ro(Sm?^y~YV0Tz2+?{QR}%n~P=;M9{gP z#FtF%2g&e|hlh?NVrg;l@{fjzbM5d4vj8oR3xX#IpufI-`)2I3cKwh_M<> zX>KYQHHe8n+6njibjt1jz>Cvp2zgV&W;D-)MlJX$L$+G|I3 zYTq?SBwPUH1 z^?(0=0TR2oHs7O+x+Rv(oqYH}@UhzslEAU~Dq&!ye#RkL6TGyNM6lWHMru zjJnwlyc*RKJAc)P80&&CmWE1ex3*Z%~Y$*HI+rnW%i%rqNvkRYKYgqod^F#9ha`2foseQBBL z0{u?ZuDpiAk*zd0wG{G$w%={-!4dhw`9KeJDT;*(qZ$euPQQDRn{=RfIt&WKIE2i- zp|RgObpV+kssyUk4Dvo|ex!1Woa}59D-Go$*pu_yZQKoB)Od(4;EK-QUz0~(o$jnY zP?KkEhnp7;!j9f0$Eg?48{l`vLnfVjrIUOd>nBb_Za6k{mxC2IDUUYnOc!m;H%Y+DhcaD0@VtBAL6&u z<4wOobb*Z&aF9AjL_~C2DV^m2#=u5NbCdT0(}fmW{PtvdUPHqrI_L6n-4pw{PLt6I;Kh#a_WEy#G`D4MwhLNI3v;MwEuc1(Tml@;b z!=~k_X*W`m!h3)Bd^R~R43$c8fk%k(iOyJ>sSrkmNj%L=<`JDy!&m1|)C7dN>b@9h z9VWd=VgK!7q6X#$Mo^0l|130f(L2h7S5?yU= z7{2hJSIXDa*(zC7K7GpfbemkzqmGr`Okmi-_fO?@2?S``COIYKSHW{ zCFv2l{6ZT=Ut8KtuT1_puJ!du@fzLKV9Oox&yzv|;!OV{2#L@Q7ONP02nmV9^m7FO zP?#_bR+eTr-#>GCBW6q@>FKoKHvV~IiTvZ_i;Sd<=hJ-zq$aGOAAlaIrHdS0U+ln? z8%)^s7n#QaF;7WZul^TA)_Cg*yb|+X_&^17K(K2aeCAet4zcxq>b0#~-)lJha3py+ zdA64M@l6;P!-!;1GF#P$avS!0*l`UXNJ`$kdAO#{HW~$UHJcNqyR^7stR`&B%0`HZ zkLJINZaHLJ6d!G`?Tyf9u{=%B#~t5jJtAL^RD5jlHQG1kHRB-CiXw7D&`Rj3A!i8R z(lM5b8t{r@@;v?XJheHhZ#xvdVY;P8?5`r}RnE!Y{dFku9p>q_8a5Qd)<-25BxHU6 zJKv3XkJyg)1@Rx77Zpid$5YjQ5A@vXdi(o++3_%yk8qH7EiLaw7+Ih|qfDpZ2s91Q zU$EscpRWJMvs5M{C(kb`+FD)J^U{EL(b6I!B}Lk85nW1lu1ix({J6E;=VetxRUFE! zxl2r+VPl%xig7*Dxw5}3Qya=)CaWUtcE_Z>S=jsFPgmuaET$sO)cju-gzR$h*+gu* z>k%J_i7L?lDlegkIh4sK5YbJ2pm07YUej-0OL1QBN$>7`^)Y!U{H?6^8=d%%pTc#* z6Kg)rST@aw{V-WS1laiaY( zL7OxIEo*GHS9hHpY(<;ea^%M2>n)XzbPthqkMj<$KZ5Cd5h_<@jw1K+xc=lbKTZ{J zzCM=BH}5UI_vDL%5~KSEL{{o~I_e*sid2}-qi-Tmq{BmGCmHX=G!H!I$9#TgtM&d~ z`@yE`=E2JmL52Ci^s1>B7bBiCItHrw&$&;n1CFgXXhK4Lc;%iDgvpwo#zmQuu`(?G zHq6?4z+mm7gd_K4SgH(Jc^*0JBcobzem}o}z%3CoC@B^v4$rhl#!ZwzNci(g*Fy{+f4&gDXeG zE#2)kic}p&apGTH0TQ8-DPN|ufnCY!S_NL zy@JUoJYLG=k-Sdl=ltu?2^#zhrZz zN^C;9Q(C$k>6Qio33&ynO(@-6(%q?)l$5lBfJlSj2Bl~5UNdLTnJ;s$`8a&wQZ{?@ zJkMQgt^5D~-PPM>++1bT$*8!yoB{nd1Am4M+UGPK%Iooo*D*Cuc+Z3vN43SKEcK>z zSYUTu1j?sS(LH}k3ks<5(NVO?B8E;E!#%=m8>Eq&`aP|L)rgoei{Vf*r8jaNKc$xQ zu5l6lsZgLAS?72erA>L?n35{u4(=^U$&aJ?$qFRatW&zrv9*{xdO9W^{rYoDNwVAB zkvoMlLjh-mE@Um{kz1Io3uEDXIs?o(n!IZzo~fIL+ePork1Wnk{(e0C)E)GxZ6QgO zk1bgd?n5LtO^v56pv#_v}Qz3D7(Bv$AKpw0}~SpGsd94R8dq+8vY4e2&0z|@8l71CCurFO?`S$OJ!fn)0;O_ z$uA7;K!kxA^kGr-Z0F{(gots4aid)&k7nh)d@h-`i1*}o6|AT}Gsygm>H6LO%h$|; zl|aGjWiXbYlC=ab#dC|$w%8{Ic{KW*4YltT%?q$rD6={7o|*}V4Sh3`fBvw{%AwJf z`V*#6xt-wBb1dDG;WtfRE<8Hx=k4U7eg`s}^B&_fX-`j2!E_n??!7-0k-Z>=NEdD` z?7FJ_>LpC3Ct;p%jSFqxD;JPM(mt=g#xu%a#y}VrD>I~vD&T@?T8oDihoXMb4DmDT zKJMJuu3e_GG9GrIWL0+d%;@?0`GRblRzWd_fn;vk{y#fU?(6hLQ~DYS$LH9MNqJa^ zl4Yns`vhAP;;qGotA93qNN!jl*uc2X8I(rfk-kLwsJ(0@L|#vOm^F{&VR-c9MC+!4 z`CrO-QH8?TgHV^j&iAHFR(Hog(+qvK(CaRo6wpfeC*!@(SFTdFp5MN1ZolK1bx?7f zq&UUJ9m`>iS`iB=J^4i5S^s9P+)h6;6c^V>hPzja1jmI|Iebu~_eDKe)W%2u;9>}M8LOI>+1gHX@@TatPT!<=6n3$4~YhOo{Se0w zY2#9pl(E!ycxc1JePhFLcQIX?-+J@DQ`&4Kl{H_=N6c?~eAd?3g!(LOv<;9L`_f=Z zFvXuh0rIQkAJ8X&$jt-wxBjW|jzq`6!JBWK)hEt0MYj57D_fTjo`({Tbjz;fnsNw} zQ^E9^X_%s^*<*fyer$42=UyecFR}5)h+dKL_$mF~6SDWzRHZ9h%9hHJp^v;XJH<_Z zef`^XTjO|1+neF`+y0t0+44NMfw&}A_eqKjg$P*UuTtQcbIx){l=?ShLM+R^04jXKO>Lvy)Q(yXONELa4*RPUXl6 zkFTuAp}8k*GfI4MD7W#;#+u-nZ`=Ht;^W6*hGxDl!Ypq>dX_3lAB4T7b(4c-&y^>r zQT5$zjAN6V2I3}kD6ZL?hyYjG>7`x%ZQ)Y926E(`jW8Z|zVBv+-fhKWQ;iHE26SBV zvIi~*BP}OcZ%08X6Jt4G|GTN7C@m}p5-2Ij=JVhXi9+| zR(NE!0GV}e+nCI(ecrIl9A$0pxTf9*ZQoe3x?7YbZb^3A1%xl6gf#XqZdI{8G@>9Z zkHn6=_fwl{`sWJn%TiXZTV?-P?vRP*a^S4I6eYsm5TWNx9{T=0YyL{Wsp97l_KDGs ziP@Nn(7W$VL9vbXKYKX8Fqt2JCG2p^UqxA%RpTpneXM*(U|(C-pMh71PjZkX%r2m7 z+BjK-G+zl{2}=F(vuF)Xi|BBD3K*1b-jTQt*m8p$Ge;Kf~M=q8Ud!ZzGZH}KCi z|IA*U=H_%-0Bh{L1lfKYzOwi4^aoEX{F-j*f2jsOEi?(AG5^x%oGHHoyI8D}+kV)mwQWRH_{PxuOpmLA#QXUVNV| zgO4!M9D zh;!}4i08?%qhdFs#fd`IGF&-uObK z_Y-9$l}#;QZ=S9$7Ksez`Ll-1i@A#$apFiR33#D_{(D#dYW%QrJrVg6tEUMzh$Te2%z=z92r zb0;>6$)}$yBJpIE&0kJU)6Y*t96ut3LOL zF0NxWD~cBuO?{YCl1e_}m`L3-8!v8Fqfh8ymNr*c912Ai=wgMZR|_FzZ!Bi>T)m<#38(c64h5DQQVLW#XbXE(z$%e}w(`_WM* zQw}7=)#hExzq-fKkvZ2Cz6-tHgH^e1Nb>?S3pu&f!YmyDA182GHwYNRr5@C)^`W&F&oljBR*#Dfe`{pT%W zH8Xq!d#Ry|$;dyEx9gPK)|wBUdDfO}ZWw7>I~vc~$yc!$Kx`E!XXS}y+9~0v(b-S= zzE>&X6O{}meYLs9O3mNgmv^*uhx|HUp#(-gSjn2dyDexV8|FE~d^>1K`}RgqCH_fB7y5T0Osx$4K&H%nhfA#tG(NArs0qljC-*|*QL3QMNF zcdPY2E3_wT$NzeNFO9}PrmNas~CU6P**w-KHKAK+~Uri5Y@w3Wkaw3l#%8U zBE^h;$ouSz+hNd_+_7%Xj&MIo+p%K{JNm+Q6}M)R%CwN967nSL`{H718$YGPkuEIN zkm_ZPe-s-W4>y?>?!u8HVKw{<>+>tHDO4yI9c+rcF5RpoA^zwsb&KqSxR8EYNPyvJ z*B6^)WHi}9ukOL;neo8>{*p%u>8~!H{^RtXJ@KMX<7Kuplg*tey)9*}bZ>+xi()Hk z_OEqGhyjdIPsc_r8EvYb!ws4PE~yAs+M4RkrS;t71$_HYmIt$>^_msUB`GNb`Pg+i z?}_fwn&!)T36d-H(dy;k#61vGilP^Z^g>Q+$af5qD%8U5k-Bi3SfBC!^eQ{wb%)t` zfh6pY3bJjb%DbUyxUcSIS%~lyc2Z|fxBVer1xOiTN8{q8Z?NPcPdop`~fcl(04Q2lDG<^bb_nYR}!fsql5xp0CyLH8h6Vc_SF zSYPcjSU=QfMZ^$rEeGF-#~$|NNB*XDD%^e*-ZA{mieZPB(^zhNOOg1ay2r5R{B>!9 z!erm}50gs`L2GwU^tErq?q9YFj)_P$cnh@gWi5W*uG)84eO}U00OA7p67oj0P%}w) zq?|CZym{ULMP6FrXvvwwW#mU-8xKjA~v~BS+VnCNEQ?;6j%`$N+LS6scu=ppeYI>5CJqg40s@?Ck!!q+; zF@?X*MKhf4a9P^?aT(T1;G&Ve%zLk5vft5Q?H!yO(l(`?SywkV_np_kk#3oA>UtgN8>rvu*V-osKWrxm$x7CqIhU-8zUJyD<1#H@ z+tMi{LS`MOI-c_e*kR?of2ur2Oeb(RHh3*2Rzfg#;%VS(dgqX^_b-wpSl#mD>F@ka zNi;{)&(4>BeYVhvzbz+QJ;obsvPG46Paq?#Vs9m@P*Yx6jla(O{dS6)Jnuj+`E1)a z--b3#rPOCbUo((fC3AS9`VwkCo&@P-+8525zxaqwJntYhy}7fyn?PGsRYlOZpOKLv z^>Dw?|D=#UmN9YA59A-*N_+b9er_HE7uA=RMB)t#6ak!Om`uasBtAKxU%44>-zRZW z#G?9|*2;f(=huGvtMYFnJ`NECCpC@@@k`pfN69}TM30*nCYQ%ibXpp>TO364Ewm=M ziNsakNFsUMLXyv-s!P6xCI_T~5L;W`4Ha@GnjBF|@MPN#j z&qwJQf(J!P?MsPqN&#K0oYI8H8EFF^G7AH>Rog@1QaWXOFb_}T2r4*2^r)!YRErr+ z!gn2hyFlA@jX-EOZc9I>fEDp8_54-MrPO5D*xz+17$s&5G72b7a9bP~NOEgwtZZ!O zy+d}#R9R?iYoU*^ zndgU*6SD85`oj|sqF%9@wD8awQ4yBE4Kv0aHohNu|Hu`td}1WTkUoK>X=}$Pu-``T z2mhCC8*OfBwfM=GS?~3^qsfwu$ukU)th9MH>PWhKjMmnXFO=)2EDwa%?OtJO*ahzF zJY{UViI=kHLFUknr>VdB?WYD!;evm$zl5+DzrQX2oTlYiiwZ`zc48rqasdz>_VMuSD*dI`ohew&1S=N+s7L~;GQPfR!1!3qvLmi8qW5iK~6!Sr0sQZf>@u&~fR5u#gd&*DP=!{*SnFu=o4 zyK^4$iO^9l`zdP!n4#uOv$yV3GtZrk51{a&HqjL%#mTIr`<}RhXYuV<=oo*-uJ4$H zWMP?EhiTBrroN?0RzbNnn<|2=(%`sjq5&3Z@~3z^JQOG3hE+?5TUI!aujP_7=U-gG#4oY}DaA8Bt7M;HqIJ=ubGV z5x~t@P}p6YV`r2hN>)4Fq)3w}NA1VVClIbHAWnbQToB5*sYquZikNnmcgaoq`zv5Usj`o zaK)y{zR#bxLf$i){KBm|rzd7stDja0dxMcfgfOwXdXt^tLB^|%e_DD!yfL&NwJe^? zygIBzAQw)GSH#*=nRxFOD!p=v(G9 zm!YE3{CzkVgy@gJlKOzf&<8LEA0&E?D(A!G(A^EcS-r= z#M3;t137n(P=8^E1o*%#tdVZgr@NIK;+D407b_j(_Lg z>z!kLr{40$Jc40!`|;$IhWP0rN4BTpRVnOV7zuHW81$^9Pi>xUJn>o&9?QoLzRnAo z$a@z>R|943T6LO+jboX$O+msDe0H>(acof>t`T)JvQ?f>^FFU-2a0|xBr|f@RmRVRhrYI}E=itI8DoFhVV@6!h$ zCI_w1{%O0WUhH0lzM$_QqG~FtXFWZS2J`V{GkO1X$#vh7#1j1s=It#x>-gE8v$Had zAi>xqqo)>;{Mxqpc9aDZG}b&SS}xvwE_1ziWg3Uj(PkdMQ_qj$Hi6ogVBv<;sEeCK zX0ycE;c!v-qcL2n#f`PO^R$s(dzS!=JK`~9t7?WRcQqykV}EWi($Ut$U90%9-isYB z`Ygj*AlVB8gXIEZdVqb)8qz;bq{ z0>?H*bck1^=nu(A!_WMJ9(Un%b1cbwvQ*_ivCZud+gG&bbf*Htuc7(e$kP<~pjMlb zE24eUgy#R8shrKq|CP+=qqgGDnx^@RdDjANp{Z6==MRvMkgD9=2`MQ%E+r>%RO&gU zmnO7*`qWiM+kr)Gcw&UFQJ7MzO*rv+P!Llz$44}@Uz+dKEk_i|pyf5VK8))6@(td3;L+MMVpo?T z{PK!i&jz4@_LKcV;QF+9?0`GW|MHLjWdn%lkvYC+`+eY&5%Lp;w+(!XDvTSp+5+^7 z>yM||*?8jkaxuUZvaZs-l6Q41`h!3|SN5%_Hwr6- zJpcEdL_HfTh0~E(0n}I${!)vdtZJkM9>8_w7xBMf6v;%Dj#G{CVP|XMW1$28=CPI* z0XomT*??IU=ugG*t*osH(8stPJUp}~xCwNgysANTca`9Xuw08gJ=g&DHt;=+K%R+_5-TyD>>z2N9I%72$>ZCBLTYk1vi!daPRSynxl|$DUQS zK5NJ_smuhaQZ7;CIJb-j`~&@UmQ592+t3pj=0|)=6BYuQ9AqLdIQ8jsVWZ)rht*E+ zlm>RLZCt_V>dxwYg!|#MfG6xWtNPbqdYF;a)EZo^Rg@XDmdQi^&tY>vXpws7JNyC zT0plhq5P&Nl@G7~JR)B;P+WsZDW=xln=XIc_VMGQn7p4kR&c zaVodW$XM4bA+sSQoZw3;olO)(di0Uu%m{GkNU@wqrqQYR=^0>}0V~?w*h2Ew5F*N%5$G|7IuCMTm zSfQx(>fRF2MNkxc*-WfZVx(N$e{~1rh8^b;{X}faCXqC>PoZYEM1lNUhUE4@+MmQi~vt1o(-@Z6qTX5#&rZnrYkJM~q?34FaNR{^C<&*R|t zn+FdN0Xd-;^FcYx9oBE1a@P;ihyUm;7@YIl4>WvkCxSfKv4 zZ)B5F-XTTZR&Ihj;vq+{I3rUgF>Tm*#D{^hu$)OHJBk8L8mp;-FE!dO^4L+iIDuS4 zu`!BTb@m>9@*+lJ21T`H15LH9>>b6|8ElaOb^?%g9D+(o@b zS1(IZ)c9)EUUF?rK=VexgvjD<+HH@9wMT0(ZgbBWum=Lh2+&p<#K4l z)HN?!Xr!}g!^m;oIU5Jk52miEsw;shiGHMDJS8WK_H`9YZwh-tk6v))EPKMF6JpSW zrG_^j>n`1tjM~z5ytEH>>^S*|v>fM*MR%(6!S3cb@6u$oicGoj1Ki{06c#LfZ+(OE z5nBPW9+$X>(|JUbUo8YqWi9;9l}Sj{H@DQ|hL_P_%FlfIRGf4y!$`**T0ldJBzOK| zA{8m~#pcN!8KOP{B5`i13GO>%_~_!01akjioF~sI6He;*t;lA$@L8;CcYl@SRUp+V zR{@dCT|~UU^KrBmo8DpZa=k6l>l~Yn+89M93R)$*d|Ss{>pDYR9`f5C0;@{1t6e9b zVVEaRG6q%55$3-?pAaZZQYE6pN~3lzc`@OKqBTfVe|s~Pw#e32t9Z3WNzYInU3}2W zGaGTrna^vBFz_eQBe{EQ7;R~x)#*DNr>-d_iH{>0i2)ZL*&A;_;~F9J#lm}gH^n+q zM#}%CK*5&+q0-ixg2#JR-qvk9MlqUl1Al&knB&1Ttz0~(9eu7Pty} z)#WGgiNsxSc*vvU-CqjZN1*psGP_i(&HUi9`W+`R)dM4VE~unk$ihHFJ~oesF<(CU zEHF7Dq5nsVj$BN2W8N^^dosq$VnvzQdqf7GYYGd9q;rVEqKRZ;f{^3|eC(nRUJGSd zGe5g`_bp#v+3XA=KMx0)O)6RO1AoMPa`LzZC$I{~YK}I96oZG{v(hjtF6(OS(x~jB zs$!4yj5QEbS+Gc3Eh%fd=nb_~e7ai!H@hTPZIJKL~FIoB||4E&f zXb3%fQE@o*v+!d0N5_qF1x9$N$wTIhbe7zVx#bE%bkU>WjinlTpBzCyh!vboTa_R8 zP%e&^SD|HcvPG$o0^zRp)ILG7NJ)d~v2AFu8qw$K<5rH;!@+L`12jp4m~p}9@#?+U z4Xg1^6H1tTHVEh`sguvN)gjebDVI9!y_7h;SdSFiqOdp3 zm09v+k7LJBXM_D%$m};R3XJ*FBd8QgX&U}=OU}XjKTsu76sxLCWkuNXG2XS`S;A9w z1G`>xynUgiD_6uLAQkiLHjezFr~+tSw*DB);^(ze40P6Wk*Wq>vz{$no89c~)ncqO zR5OTI_dd6OHdjq{a6)Ua*?hY#|m;2dvAlj-ubC;g`vG9DO@{z8*+$@i6AtSaten~S^Wr+s1K^PrI z?nWEOZCtd}*NaYQ9=%*Yl8Y^CQVWROwa`u}Adc<{Hb*Eeacga_pk)z>r}F$?0&A@w zqOZVvqf=o7ghF0Y91sQVuCG^$nqnBkVt%T~p*j;D~ zr~VYq)~>6gGY!sA@KUQ0?Jaf~mr6olrfP?rT236c${j;=u>*I+zkA2SoRn8gtiu~Z z1tMuRt$X6{6zLxgR$sGvd;{$tzg(NlzaWrK93(&&$A1HqlW0HRux0Sfv2~oOJ7L8A zM<9F{2+4&irS$WCE)Z7da-4zd@U+}Rf)Ff#8XTbTW>&-W>uCQR#C2U1|j zIv7+ky7voNJ_K*xi!)2k$Y=-p->+YlV>Vw0z7i%Y%BaOTxh@}CNurBiEKyyUs^C%u zUr!K1m^1}mcZ_BkAjk!RF0+nFXq*oiayN;|RB(Gk-`n3j~=n91$QKbnmed43vLcfkc5}!KP zQ~k$h`0TXAT{W`IkyQ|6e4Ptu|0Ux)EqS~6z@YOL$l6<4e0+TkJHQFu(%cNDrfjhn z|3@arE-f-shU{Cpf`@wU839o*NCLLrfY0i0S0iDY0;DltplBv1lK~OwtJ??=QWLjr z28OYL^fUAAn+UY;o)vE@d^}k1qg|Q518);7v#=RLE4R!K?2)pjrsM+K^H~w+yP2w; zZ7z2U3k!XGe5gcy5ps72tAUeX4+nb8+CPm}rn>knv}Zp^5#J6bSmX0eolM^YIl*+; zF1m6!h~op@W>qLi4i56_IG^tTXZ5Uoy*+P9vE9FENA%Mla8hIkU_UblR{@5BiGcwJ zu2JJ7!e3QE1_@G@y}i9lNbRhxt-)iR;~y%Pt-9p=*IMj!7PN{MdblB70J;F0rD$fw zjP&e`)lI-zy#%K;2{AEz5^rjc%N3nsWuF>L-RS1kC>1?NP4x6g%f)it^Iu(fKHlD+Gek~vsm>Aj zSm2lJHmic8%s&446H~2s4HFX+62q(4r~S{Tz0(_;o5{P_rPT&X{)wU0($Vn;H~#rl zL;LEXHyEKybXlfmXXPeP3;ba!i0|+nBk;r8?{rbW6JY$<01jhk2jHUrpJ# zN&8X8WXjEL0(1fJnuSok^18e@hc0eL^=+C#ZXD)C9;F)nGMG6<0rQZ9fVKcABZ$so z7We)f`vNaW1Ws||rv&60>(Rbw`moi+JhABUkJzA~o9E(kF}%z~N>`+k-(JEnX}MZj z=Fjh34Pnr(fNjVtgUwEjx8C&Hi+!&%m^MMAxa3@0R|gQkM_3P$W-sqmLZu=*VOOpb>Rm1$DgI93)s-BzMgTNR9 z`yiZ#2P6;wzg<)d;GzeQJ3L*uwv4sIm>k5WM~fQKE|V7kif% zsf%YY>DcoQvJV+C6}1=q4h{mTCRpNZxQWZz5^P_B1LzqmNf&!c!5ti&*2|02j`JO- zt*x!WhJRqV0Dv;f_5|pT7JAzFZ$v!qP7~qJN;0TT)M_%pc}HH*uFiJ zc-1^t2OiSOnUT_C1&rAU144Omuh7;TJf%=D1H1-+3vIZs9Pr@5>JeRdOd!1v2TWcK z$4Cl3DK%dJYs14b_SvZexPy}}g0c@*5HPY{-5=mKkU2mwZa@9DmtZo@_$~P6RZIXY z#uZd_J&_OOh%i>*Z_b0u?K&U`E|*CL1pv!k+(g6MUWF=_twV1Bj1}0zReP6|T%2ka zpnB19x^xrDDk?4){z(l zVdxG&Yph4=8yYAgW%9maH`dm6|Mms#;b8TNbT}0G!1x{8k9xB>#L2W`b(1@FxS9oS z4EkKVHipuKXyzW=_z}PsM}GTODN~hxg@`fBeJ62&5aP1|g8y!TIDR>a6YVI^ko6Ck zqoBSk?2US=r4_XTZDs%*@sksv^k5ItTu0LmjJs}<2$kM{E4HF8xX2tZu2-`FDG=NZ zc(}N{JUmc{u6zK0xkWr-6=uZl~W>?Wc@r z9)Sh6Kw9UII`tJz91O~NZ{!98bDZSoSmEK&s%rrC*-=ABo9{8F(gbPnH{gc>bpuoE zuQ)aS`tI%pz>Qx3aSTqV#QF$C0Q0IDU`F+`D^?9+b}GMyTLpnmFmPuUn_p}(h#mK5 zONO)>Ir;hdL7WNrVlcq*0?LNZ|2H3~qtV4-g*9yT+J|Qd5g~>+ex~2iETENuyWWl~ z!I@lIR(8IG=br?nqC?}$L+FmezTjW%3JHHEispfR^~kPCk4+S+Ji>B?7z}~|XbOCE z7vQs8nO}^NsivfU!53F0`*uHyzJ0*hUrOOycz7ILG3lv9HLs$4pd*~1HsZj0>_8B(Kokfd@lLCnvRMaPTRCHD4&rd|E^w2N zqzP!~)_gSsl6WUnSuU1~m>N#fN9`LN?mBrZBSIPjznU!qhlxj8ulauCRz zAU{QV4F`uESfZ#hgXft=JS@3Xhq;khh z*lp_cHHrY-pH*BeJn}pR*5vK&(vh`CSUF8i5Wx=Wlp7Y6l)&22Z{h0UF}2~oxh4NL zA_AWF*vkIXm@g9(k`wNL%JW!4n-PaCkZl+o4`2cT$y`Q(Eu=+*2Y`2hqj;mJ#6*D! z4yYaQMqWZ|cmZnw*m_rVY&a9(5)frIukr!t)&^!rZ%~KA@wNZ%SybtQ?`R)5wC~hS zoUTT}Ro94$!u4Z!Vu~McQ3k*b#`SB^1*tzl7yj3+BRtY^ak2$t6ympjix|t<=4L^W z9DHZ(2Xr+R;d>e9>E`4^baYypOKRE1WlU{bGl zl`h`aKSM)M!0g5TjcUdzs|W8YsR$qz`}Z| Date: Thu, 28 Apr 2022 16:03:04 -0400 Subject: [PATCH 07/11] swap rows and columns back for easier import into pandas --- src/fix_ave_time.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fix_ave_time.cpp b/src/fix_ave_time.cpp index 51e201000c..60e72b6640 100644 --- a/src/fix_ave_time.cpp +++ b/src/fix_ave_time.cpp @@ -910,11 +910,11 @@ void FixAveTime::invoke_vector(bigint ntimestep) fputs("data:\n", fp); } fmt::print(fp, " {}:\n", ntimestep); - for (i = 0; i < nvalues; i++) { - for (j = 0; j < nrows; j++) { + for (i = 0; i < nrows; i++) { + for (j = 0; j < nvalues; j++) { if (j == 0) fputs(" - - ", fp); else fputs(" - ", fp); - fmt::print(fp,"{}\n",array_total[j][i]/norm); + fmt::print(fp,"{}\n",array_total[i][j]/norm); } } } else { From bf8f5e2f875c2b07f31952f7f67c46488e5967b2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 28 Apr 2022 18:08:59 -0400 Subject: [PATCH 08/11] more compact output --- src/fix_ave_time.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/fix_ave_time.cpp b/src/fix_ave_time.cpp index 60e72b6640..010170e149 100644 --- a/src/fix_ave_time.cpp +++ b/src/fix_ave_time.cpp @@ -899,23 +899,15 @@ void FixAveTime::invoke_vector(bigint ntimestep) if (yaml_flag) { if (!yaml_header || overwrite) { yaml_header = true; - fputs("keywords:\n - Step\n", fp); - bool first = true; - for (auto k : keyword) { - if (first) { - first = false; - fmt::print(fp, " - - '{}'\n", k); - } else fmt::print(fp, " - '{}'\n", k); - } - fputs("data:\n", fp); + fputs("keywords: [", fp); + for (auto k : keyword) fmt::print(fp, "'{}', ", k); + fputs("]\ndata:\n", fp); } fmt::print(fp, " {}:\n", ntimestep); for (i = 0; i < nrows; i++) { - for (j = 0; j < nvalues; j++) { - if (j == 0) fputs(" - - ", fp); - else fputs(" - ", fp); - fmt::print(fp,"{}\n",array_total[i][j]/norm); - } + fputs(" - [", fp); + for (j = 0; j < nvalues; j++) fmt::print(fp,"{}, ",array_total[i][j]/norm); + fputs("]\n", fp); } } else { fmt::print(fp,"{} {}\n",ntimestep,nrows); From 7d9cdc7ec499dd959273e323efeda86a3380930b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 28 Apr 2022 18:34:49 -0400 Subject: [PATCH 09/11] update documentation. more examples. document fix_modify options --- doc/src/Howto_structured_data.rst | 59 +++++++++++++++++++-- doc/src/fix_ave_time.rst | 20 ++++--- doc/src/fix_modify.rst | 25 ++++++++- doc/utils/sphinx-config/false_positives.txt | 1 + 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/doc/src/Howto_structured_data.rst b/doc/src/Howto_structured_data.rst index 6b45f945c5..deadb72fc7 100644 --- a/doc/src/Howto_structured_data.rst +++ b/doc/src/Howto_structured_data.rst @@ -112,20 +112,28 @@ of that run: Number of runs: 2 TotEng = -4.62140097780047 -Processing/plotting extracted data with Pandas ----------------------------------------------- +.. versionadded:: 4May2022 + +YAML format output has been added to multiple commands in LAMMPS, +for example :doc:`dump yaml ` or :doc:`fix ave/time ` +Depending on the kind of data being written, organization of the data +or the specific syntax used may change, but the principles are very +similar and all files should be readable with a suitable YAML parser. + +Processing scalar data with Pandas +---------------------------------- .. figure:: JPG/thermo_bondeng.png :figwidth: 33% :align: right -After extracting the YAML format data and parsing it, it can be easily -imported for further processing with the `pandas +After reading and parsing the YAML format data, it can be easily +imported for further processing and visualization with the `pandas `_ and `matplotlib `_ Python modules. Because of the organization of the data in the YAML format thermo output, it needs to be told to process only the 'data' part of the imported data to create a pandas -dataframe, and one needs to set the column names from the 'keywords' +data frame, and one needs to set the column names from the 'keywords' entry. The following Python script code example demonstrates this, and creates the image shown on the right of a simple plot of various bonded energy contributions versus the timestep from a run of the 'peptide' @@ -158,6 +166,47 @@ colname ` command. fig = df.plot(x='Step', y=['E_bond', 'E_angle', 'E_dihed', 'E_impro'], ylabel='Energy in kcal/mol') plt.savefig('thermo_bondeng.png') +Processing vector data with Pandas +---------------------------------- + +Global *vector* data as produced by :doc:`fix ave/time ` +uses a slightly different organization of the data. You still have the +dictionary keys 'keywords' and 'data' for the column headers and the +data. But the data is a dictionary indexed by the time step and for +each step there are multiple rows of values each with a list of the +averaged properties. This requires a slightly different processing, +since the entire data cannot be directly imported into a single pandas +DataFrame class instance. The following Python script example +demonstrates how to read such data. The result will combine the data +for the different steps into one large "multi-index" table. The pandas +IndexSlice class can then be used to select data from this combined data +frame. + +.. code-block:: python + + import re, yaml + import pandas as pd + + try: + from yaml import CSafeLoader as Loader + except ImportError: + from yaml import SafeLoader as Loader + + with open("ave.yaml") as f: + ave = yaml.load(docs, Loader=Loader) + + keys = ave['keywords'] + df = {} + for k in ave['data'].keys(): + df[k] = pd.DataFrame(data=ave['data'][k], columns=keys) + + # create multi-index data frame + df = pd.concat(df) + + # output only the first 3 value for steps 200 to 300 of the column Pressure + idx = pd.IndexSlice + print(df['Pressure'].loc[idx[200:300, 0:2]]) + Writing continuous data during a simulation =========================================== diff --git a/doc/src/fix_ave_time.rst b/doc/src/fix_ave_time.rst index dfa385ea46..4b25e7c174 100644 --- a/doc/src/fix_ave_time.rst +++ b/doc/src/fix_ave_time.rst @@ -258,10 +258,16 @@ each input value specified in the fix ave/time command. For *mode* = scalar, this means a single line is written each time output is performed. Thus the file ends up to be a series of lines, i.e. one column of numbers for each input value. For *mode* = vector, an array -of numbers is written each time output is performed. The number of -rows is the length of the input vectors, and the number of columns is -the number of values. Thus the file ends up to be a series of these -array sections. +of numbers is written each time output is performed. The number of rows +is the length of the input vectors, and the number of columns is the +number of values. Thus the file ends up to be a series of these array +sections. If the filename ends in '.yaml' or '.yml' then the output +format is conforming to the `YAML standard `_ which +allows to easily import that data into tools and scripts that support +reading YAML files. The :doc:`structured data Howto +` contains examples for parsing and plotting such +data with very little programming effort in Python using the *pyyaml*, +*pandas*, and *matplotlib* packages. The *overwrite* keyword will continuously overwrite the output file with the latest output, so that it only contains one timestep worth of @@ -307,8 +313,10 @@ appropriate fields from the fix ave/time command. Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" -No information about this fix is written to :doc:`binary restart files `. None of the :doc:`fix_modify ` options -are relevant to this fix. +No information about this fix is written to :doc:`binary restart files +`. The :doc:`fix_modify colname ` options can be +used to change the name of the column in the output file. When writing +a YAML format file this name will be in the list of keywords. This fix produces a global scalar or global vector or global array which can be accessed by various :doc:`output commands `. diff --git a/doc/src/fix_modify.rst b/doc/src/fix_modify.rst index 47080fcd58..9f5f0b3d90 100644 --- a/doc/src/fix_modify.rst +++ b/doc/src/fix_modify.rst @@ -12,7 +12,7 @@ Syntax * fix-ID = ID of the fix to modify * one or more keyword/value pairs may be appended -* keyword = *temp* or *press* or *energy* or *virial* or *respa* or *dynamic/dof* or *bodyforces* +* keyword = *temp* or *press* or *energy* or *virial* or *respa* or *dynamic/dof* or *bodyforces* or *colname* .. parsed-literal:: @@ -25,6 +25,11 @@ Syntax yes/no = do or do not re-compute the number of degrees of freedom (DOF) contributing to the temperature *bodyforces* value = *early* or *late* early/late = compute rigid-body forces/torques early or late in the timestep + *colname* values = ID string + string = new column header name + ID = integer from 1 to N, or integer from -1 to -N, where N = # of quantities being output + *or* a fix output property keyword or reference to compute, fix, property or variable. + Examples """""""" @@ -34,6 +39,7 @@ Examples fix_modify 3 temp myTemp press myPress fix_modify 1 energy yes fix_modify tether respa 2 + fix_modify ave colname c_thermo_press Pressure colname 1 Temperature Description """"""""""" @@ -165,6 +171,20 @@ will have no effect on the motion of the rigid bodies if they are specified in the input script after the fix rigid command. LAMMPS will give a warning if that is the case. + +The *colname* keyword can be used to change the default header keywords +in output files of fix styles that support it: currently only :doc:`fix +ave/time ` is supported. The setting for *ID string* +replaces the default text with the provided string. *ID* can be a +positive integer when it represents the column number counting from the +left, a negative integer when it represents the column number from the +right (i.e. -1 is the last column/keyword), or a custom fix output +keyword (or compute, fix, property, or variable reference) and then it +replaces the string for that specific keyword. The *colname* keyword can +be used multiple times. If multiple *colname* settings refer to the same +keyword, the last setting has precedence. + + Restrictions """""""""""" none @@ -172,7 +192,8 @@ none Related commands """""""""""""""" -:doc:`fix `, :doc:`compute temp `, :doc:`compute pressure `, :doc:`thermo_style ` +:doc:`fix `, :doc:`compute temp `, +:doc:`compute pressure `, :doc:`thermo_style ` Default """"""" diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 62983f4f39..81d9ff34c9 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -3754,6 +3754,7 @@ ylat ylo ymax ymin +yml Yoshida ys ysu From 514bfe77dfee01462d9d225ab88af3981d56918a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 29 Apr 2022 17:33:37 -0400 Subject: [PATCH 10/11] apply changes suggested by @sjplimp --- doc/src/fix_ave_time.rst | 18 ++++++++++-------- doc/src/fix_modify.rst | 17 ++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/doc/src/fix_ave_time.rst b/doc/src/fix_ave_time.rst index 4b25e7c174..674a431e0d 100644 --- a/doc/src/fix_ave_time.rst +++ b/doc/src/fix_ave_time.rst @@ -261,13 +261,15 @@ column of numbers for each input value. For *mode* = vector, an array of numbers is written each time output is performed. The number of rows is the length of the input vectors, and the number of columns is the number of values. Thus the file ends up to be a series of these array -sections. If the filename ends in '.yaml' or '.yml' then the output -format is conforming to the `YAML standard `_ which -allows to easily import that data into tools and scripts that support -reading YAML files. The :doc:`structured data Howto -` contains examples for parsing and plotting such -data with very little programming effort in Python using the *pyyaml*, -*pandas*, and *matplotlib* packages. +sections. + +If the filename ends in '.yaml' or '.yml' then the output format +conforms to the `YAML standard `_ which allows +easy import that data into tools and scripts that support reading YAML +files. The :doc:`structured data Howto ` contains +examples for parsing and plotting such data with very little programming +effort in Python using the *pyyaml*, *pandas*, and *matplotlib* +packages. The *overwrite* keyword will continuously overwrite the output file with the latest output, so that it only contains one timestep worth of @@ -314,7 +316,7 @@ Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" No information about this fix is written to :doc:`binary restart files -`. The :doc:`fix_modify colname ` options can be +`. The :doc:`fix_modify colname ` option can be used to change the name of the column in the output file. When writing a YAML format file this name will be in the list of keywords. diff --git a/doc/src/fix_modify.rst b/doc/src/fix_modify.rst index 9f5f0b3d90..e0cf3a34c7 100644 --- a/doc/src/fix_modify.rst +++ b/doc/src/fix_modify.rst @@ -12,24 +12,23 @@ Syntax * fix-ID = ID of the fix to modify * one or more keyword/value pairs may be appended -* keyword = *temp* or *press* or *energy* or *virial* or *respa* or *dynamic/dof* or *bodyforces* or *colname* +* keyword = *bodyforces* or *colname* or *dynamic/dof* or *energy* or *press* or *respa* or *temp* or *virial* .. parsed-literal:: - *temp* value = compute ID that calculates a temperature - *press* value = compute ID that calculates a pressure - *energy* value = *yes* or *no* - *virial* value = *yes* or *no* - *respa* value = *1* to *max respa level* or *0* (for outermost level) - *dynamic/dof* value = *yes* or *no* - yes/no = do or do not re-compute the number of degrees of freedom (DOF) contributing to the temperature *bodyforces* value = *early* or *late* early/late = compute rigid-body forces/torques early or late in the timestep *colname* values = ID string string = new column header name ID = integer from 1 to N, or integer from -1 to -N, where N = # of quantities being output *or* a fix output property keyword or reference to compute, fix, property or variable. - + *dynamic/dof* value = *yes* or *no* + yes/no = do or do not re-compute the number of degrees of freedom (DOF) contributing to the temperature + *energy* value = *yes* or *no* + *press* value = compute ID that calculates a pressure + *respa* value = *1* to *max respa level* or *0* (for outermost level) + *temp* value = compute ID that calculates a temperature + *virial* value = *yes* or *no* Examples """""""" From ee8d8042e6cd210b164ff200d575a452e8e0e5fa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 29 Apr 2022 17:42:23 -0400 Subject: [PATCH 11/11] add example for YAML processing with Perl --- doc/src/Howto_structured_data.rst | 37 +++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/doc/src/Howto_structured_data.rst b/doc/src/Howto_structured_data.rst index deadb72fc7..4ea6c28086 100644 --- a/doc/src/Howto_structured_data.rst +++ b/doc/src/Howto_structured_data.rst @@ -120,7 +120,7 @@ Depending on the kind of data being written, organization of the data or the specific syntax used may change, but the principles are very similar and all files should be readable with a suitable YAML parser. -Processing scalar data with Pandas +Processing scalar data with Python ---------------------------------- .. figure:: JPG/thermo_bondeng.png @@ -166,7 +166,7 @@ colname ` command. fig = df.plot(x='Step', y=['E_bond', 'E_angle', 'E_dihed', 'E_impro'], ylabel='Energy in kcal/mol') plt.savefig('thermo_bondeng.png') -Processing vector data with Pandas +Processing vector data with Python ---------------------------------- Global *vector* data as produced by :doc:`fix ave/time ` @@ -208,6 +208,39 @@ frame. print(df['Pressure'].loc[idx[200:300, 0:2]]) +Processing scalar data with Perl +-------------------------------- + +The ease of processing YAML data is not limited to Python. Here is an +example for extracting and processing a LAMMPS log file with Perl instead. + +.. code-block:: perl + + use YAML::XS; + + open(LOG, "log.lammps") or die("could not open log.lammps: $!"); + my $file = ""; + while(my $line = ) { + if ($line =~ /^(keywords:.*$|data:$|---$|\.\.\.$| - \[.*\]$)/) { + $file .= $line; + } + } + close(LOG); + + # convert YAML to perl as nested hash and array references + my $thermo = Load $file; + + # convert references to real arrays + my @keywords = @{$thermo->{'keywords'}}; + my @data = @{$thermo->{'data'}}; + + # print first two columns + print("$keywords[0] $keywords[1]\n"); + foreach (@data) { + print("${$_}[0] ${$_}[1]\n"); + } + + Writing continuous data during a simulation ===========================================