diff --git a/Bin/Config/Common/ModelParaDefinesNM_cn.dat b/Bin/Config/Common/ModelParaDefinesNM_cn.dat
index 3b97d08..4ad2fde 100644
--- a/Bin/Config/Common/ModelParaDefinesNM_cn.dat
+++ b/Bin/Config/Common/ModelParaDefinesNM_cn.dat
@@ -1 +1 @@
-PD94bWwgdmVycup_mpac2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCg0KPCEtLSoqKioqKioqKioqKiwzyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiotLT4NCjwhLS0gICAg5pys5paH5qGj55qE55uu55qE5piv5bu656uLV1RBSeaVsOWAvOinoyhOTSnmqKHlnZfnmoTlj4LmlbDlrprkuYkgICAgICAgICAgICAgICAgLS0+DQo8IS0tICAgIOS7heWMheWQq1BFQknmsYLop6PlmajmiYDpnIDnmoTlgqjlsYLjgIHkupXlkozlh6DkvZXlr7nosaHlj4LmlbAgICAgICAgICAgICAgICAgICAtLT4NCjwhLS0gICAgRGF0ZTogMjAyNjA2MTUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPg0KPCEtLSoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiotLT4NCg0KPCEtLQ0KICAgIOWPguaVsOiMg+WbtO+8mlBFQknmsYLop6PlmajmiYDpnIDnmoTlgqjlsYLjgIHkupXlkozlh6DkvZXlr7nosaHlj4LmlbANCiAgICDlr7nlupQgSFhfTldUTV9NT0RFTF9JTlBVVCDnu5PmnoTkvZPlrZfmrrUNCiAgICDpgJrov4cgX3BhcmFIZWxwZXItPmxvYWRQYXJhRGVmaW5lcygpIOWKoOi9ve+8jHNLZXkg5qCH6K+G5Li6IHNfTm1fU2VyaWUNCi0tPg0KDQo8Um9vdD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IuWCqOWxguWPguaVsCI+DQogICAgPFBhcmFJdGVtIE5hbWU9ImgiICAgQWxpYXM9IuWCqOWxguWOmuW6piIgICAgICAgVW5pdD0ibSIgICAgIERlZmF1bHQ9IjEwIiAgICBNYXg9IjEwMDAwIiAgTWluPSIxZS0wNSIgIERpZ2l0PSI2IiAgRGVzYz0icmVzZXJ2b2lyIHRoaWNrbmVzcyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUGkiICBBbGlhcz0i5Yid5aeL5Y6L5YqbIiAgICAgICBVbml0PSJNUGEiICAgRGVmYXVsdD0iNDAiICAgIE1heD0iNTAwIiAgICBNaW49IjAuMSIgICAgRGlnaXQ9IjYiICBEZXNjPSJpbml0aWFsIGZvcm1hdGlvbiBwcmVzc3VyZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iSyIgICBBbGlhcz0i5riX6YCP546HIiAgICAgICAgIFVuaXQ9Im1EIiAgICBEZWZhdWx0PSIxIiAgICAgTWF4PSIxMDAwMCIgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InBlcm1lYWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0icGhpIiBBbGlhcz0i5a2U6ZqZ5bqmIiAgICAgICAgIFVuaXQ9IiIgICAgICBEZWZhdWx0PSIwLjEiICAgTWF4PSIxIiAgICAgIE1pbj0iMWUtMDUiICBEaWdpdD0iNiIgIERlc2M9InBvcm9zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJDdGkiIEFsaWFzPSLnu7zlkIjljovnvKnns7vmlbAiICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAuMDAxIiBNYXg9IjEwIiAgICAgTWluPSIxZS0zMCIgIERpZ2l0PSIxMCIgU2NpZW50aWZpYz0iMSIgRGVzYz0idG90YWwgY29tcHJlc3NpYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJDZiIgIEFsaWFzPSLlsqnnn7PljovnvKnns7vmlbAiICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAuMDAwMSIgTWF4PSIxMCIgICAgTWluPSIxZS0zMCIgIERpZ2l0PSIxMCIgU2NpZW50aWZpYz0iMSIgRGVzYz0icm9jayBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlNvaSIgQWxpYXM9IuWIneWni+WQq+ayuemlseWSjOW6piIgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuOCIgICBNYXg9IjEiICAgICAgTWluPSIwIiAgICAgIERpZ2l0PSI2IiAgRGVzYz0iaW5pdGlhbCBvaWwgc2F0dXJhdGlvbiIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iU3dpIiBBbGlhcz0i5Yid5aeL5ZCr5rC06aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMC4yIiAgIE1heD0iMSIgICAgICBNaW49IjAiICAgICAgRGlnaXQ9IjYiICBEZXNjPSJpbml0aWFsIHdhdGVyIHNhdHVyYXRpb24iIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0i5LqV5Y+C5pWwIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19YIiAgICBBbGlhcz0iWOWdkOaghyIgICAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgICAgIE1heD0iMTAwMDAiICBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiICBEZXNjPSJ3ZWxsIFggY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19ZIiAgICBBbGlhcz0iWeWdkOaghyIgICAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgICAgIE1heD0iMTAwMDAiICBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiICBEZXNjPSJ3ZWxsIFkgY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19SdyIgICBBbGlhcz0i5LqV562S5Y2K5b6EIiAgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwLjEiICAgTWF4PSIxMCIgICAgIE1pbj0iMC4wMDEiICBEaWdpdD0iNiIgIERlc2M9IndlbGxib3JlIHJhZGl1cyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19Ta2luIiBBbGlhcz0i6KGo55qu57O75pWwIiAgICBVbml0PSIiICAgICBEZWZhdWx0PSIwIiAgICAgTWF4PSIxMDAwIiAgIE1pbj0iLTEwMCIgICBEaWdpdD0iNiIgIERlc2M9InNraW4gZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0MiICAgIEFsaWFzPSLkupXnrZLlgqjpm4bns7vmlbAiIFVuaXQ9Im1eMy9NUGEiIERlZmF1bHQ9IjAiIE1heD0iMTAwMCIgICBNaW49IjAiICAgICAgRGlnaXQ9IjYiICBEZXNjPSJ3ZWxsYm9yZSBzdG9yYWdlIGNvZWZmaWNpZW50IiAvPgogICAgPFBhcmFJdGVtIE5hbWU9IldfV2VsbExlbmd0aCIgICAgICAgICBBbGlhcz0i5rC05bmz5q616ZW/5bqmIiAgIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjIwMCIgIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0RyYWluQW5nbGUiICAgICAgICAgQWxpYXM9IuaOkuawtOinkuW6piIgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjAiICAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgZHJhaW4gYW5nbGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfTnVtYmVyT2ZGcmFjdHVyZXMiICBBbGlhcz0i6KOC57yd5pWw6YePIiAgICAgVW5pdD0iIiAgICAgRGVmYXVsdD0iMTUiICAgTWF4PSIxMDAwIiAgTWluPSIyIiAgICAgRGlnaXQ9IjAiIERlc2M9Im51bWJlciBvZiBmcmFjdHVyZXMiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfRnJhY3R1cmVIYWxmTGVuZ3RoIiBBbGlhcz0i6KOC57yd5Y2K6ZW/IiAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iNTAiICAgTWF4PSIxMDAwMCIgTWluPSIxZS0wNSIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGhhbGYtbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0ZyYWN0dXJlQW5nbGUiICAgICAgQWxpYXM9Iuijgue8neinkuW6piIgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjkwIiAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBhbmdsZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19EZmMiICAgICAgICAgICAgICAgIEFsaWFzPSLoo4LnvJ3lr7zmtYHog73lipsiIFVuaXQ9Im1kLm0iIERlZmF1bHQ9IjEwMDAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBjb25kdWN0aXZpdHkiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0i54us56uL6KOC57yd5Y+C5pWwIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRl9YMCIgQWxpYXM9Iui1t+eCuVgwIiAgIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgc3RhcnQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGX1kwIiBBbGlhcz0i6LW354K5WTAiICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBzdGFydCBZIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfWDEiIEFsaWFzPSLnu4jngrlYMSIgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGVuZCBYIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfWTEiIEFsaWFzPSLnu4jngrlZMSIgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGVuZCBZIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfRkMiIEFsaWFzPSLlr7zmtYHog73lipsiIFVuaXQ9Im1kLm0iIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgY29uZHVjdGl2aXR5IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IuaWreWxguWPguaVsCI+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZUX1gwIiBBbGlhcz0i6LW354K5WDAiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZmF1bHQgc3RhcnQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9ZMCIgQWxpYXM9Iui1t+eCuVkwIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZhdWx0IHN0YXJ0IFkgY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRlRfWDEiIEFsaWFzPSLnu4jngrlYMSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBlbmQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9ZMSIgQWxpYXM9Iue7iOeCuVkxIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZhdWx0IGVuZCBZIGNvb3JkaW5hdGUiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0i55+p5b2i6L6555WM5Y+C5pWwIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1pbiIgQWxpYXM9IuW3pui+ueeVjFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1pbmltdW0gWCIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWU1pbiIgQWxpYXM9IuS4i+i+ueeVjFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1pbmltdW0gWSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1heCIgQWxpYXM9IuWPs+i+ueeVjFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1heGltdW0gWCIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWU1heCIgQWxpYXM9IuS4iui+ueeVjFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1heGltdW0gWSIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSLlnIblvaLovrnnlYzlj4LmlbAiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19DZW50ZXJYIiBBbGlhcz0i5ZyG5b+DWCIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJjaXJjbGUgYm91bmRhcnkgY2VudGVyIFgiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkJDX0NlbnRlclkiIEFsaWFzPSLlnIblv4NZIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImNpcmNsZSBib3VuZGFyeSBjZW50ZXIgWSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQkNfUmFkaXVzIiAgQWxpYXM9IuWNiuW+hCIgIFVuaXQ9Im0iIERlZmF1bHQ9IjEiIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiICBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IHJhZGl1cyIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSLlpJrovrnlvaLovrnnlYzngrnlj4LmlbAiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUF9YIiBBbGlhcz0iWOWdkOaghyIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJwb2x5Z29uIGJvdW5kYXJ5IHZlcnRleCBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUF9ZIiBBbGlhcz0iWeWdkOaghyIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJwb2x5Z29uIGJvdW5kYXJ5IHZlcnRleCBZIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IuWMuuWfn+WPguaVsCI+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJfTGVha2FnZSIgQWxpYXM9IuazhOmcsuezu+aVsCIgVW5pdD0iIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiIERpZ2l0PSI2IiBEZXNjPSJyZWdpb24gbGVha2FnZSBjb2VmZmljaWVudCIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSLmoIforrDljLrln5/lj4LmlbAiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSTV9Db21XIiAgICAgICBBbGlhcz0i5YKo6IO95q+UIiBVbml0PSIiIERlZmF1bHQ9IjEiIE1heD0iMSIgICAgTWluPSIwIiAgICAgRGlnaXQ9IjYiIERlc2M9InN0b3JhZ2UgcmF0aW8iIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJNX0NvbUtyIiAgICAgIEFsaWFzPSLmtYHluqbmr5QiIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxMDAwIiBNaW49IjAuMDAxIiBEaWdpdD0iNiIgRGVzYz0ibW9iaWxpdHkgcmF0aW8iIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJNX05ldFRvR3Jvc3MiIEFsaWFzPSLlh4Dmr5vmr5QiIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxIiAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgRGVzYz0ibmV0LXRvLWdyb3NzIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8IS0tIOe7k+aenOmdouadv+WPguaVsOWPqueUsSByZWJ1aWxkUmVzdWx0UGFyYXMoKSDpgInmi6nvvIzkuI3kvJrlh7rnjrDlnKjlt6bkvqfpnaLmnb/kuK3jgIIgLS0+DQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0i57uT5p6c5Y+C5pWwIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1dfUnciICAgQWxpYXM9IuS6leW+hCIgICAgICAgICBVbml0PSJtIiAgICAgIERlZmF1bHQ9IjAuMSIgICAgTWF4PSIxMCIgICAgTWluPSIwLjAwMSIgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHdlbGxib3JlIHJhZGl1cyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1dfRGZjIiAgQWxpYXM9Iuijgue8neWvvOa1geiDveWKmyIgVW5pdD0ibWQubSIgICBEZWZhdWx0PSIxMDAwIiAgIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBmcmFjdHVyZSBjb25kdWN0aXZpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX1NraW4iIEFsaWFzPSLooajnmq7ns7vmlbAiICAgICBVbml0PSIiICAgICAgIERlZmF1bHQ9IjAiICAgICAgTWF4PSIxMDAwIiAgTWluPSItMTAwIiAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHNraW4gZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfV19DIiAgICBBbGlhcz0i5LqV562S5YKo6ZuG57O75pWwIiBVbml0PSJtXjMvTVBhIiBEZWZhdWx0PSIwIiAgICAgIE1heD0iMTAwMCIgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3ZWxsYm9yZSBzdG9yYWdlIGNvZWZmaWNpZW50IiAvPgoNCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1BpIiAgQWxpYXM9IuWIneWni+WcsOWxguWOi+WKmyIgICBVbml0PSJNUGEiICAgRGVmYXVsdD0iNDAiICAgICBNYXg9IjUwMCIgICBNaW49IjAuMSIgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIGZvcm1hdGlvbiBwcmVzc3VyZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0siICAgQWxpYXM9Iua4l+mAj+eOhyIgICAgICAgICBVbml0PSJtRCIgICAgRGVmYXVsdD0iMSIgICAgICBNYXg9IjEwMDAwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBwZXJtZWFiaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9oIiAgIEFsaWFzPSLlgqjlsYLljprluqYiICAgICAgIFVuaXQ9Im0iICAgICBEZWZhdWx0PSIxMCIgICAgIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHJlc2Vydm9pciB0aGlja25lc3MiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9waGkiIEFsaWFzPSLlrZTpmpnluqYiICAgICAgICAgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuMSIgICAgTWF4PSIxIiAgICAgTWluPSIxZS0wNSIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgcG9yb3NpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9DdGkiIEFsaWFzPSLnu7zlkIjljovnvKnns7vmlbAiICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAuMDAxIiAgTWF4PSIxMCIgICAgTWluPSIxZS0zMCIgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgdG90YWwgY29tcHJlc3NpYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ2YiICBBbGlhcz0i5bKp55+z5Y6L57yp57O75pWwIiAgIFVuaXQ9IjEvTVBhIiBEZWZhdWx0PSIwLjAwMDEiIE1heD0iMTAiICAgIE1pbj0iMWUtMzAiIERpZ2l0PSIxMCIgU2NpZW50aWZpYz0iMSIgRGVzYz0icmVzdWx0IHJvY2sgY29tcHJlc3NpYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU29pIiBBbGlhcz0i5Yid5aeL5ZCr5rK56aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMC44IiAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIG9pbCBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU2dpIiBBbGlhcz0i5Yid5aeL5ZCr5rCU6aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMCIgICAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIGdhcyBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU3dpIiBBbGlhcz0i5Yid5aeL5ZCr5rC06aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMC4yIiAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIHdhdGVyIHNhdHVyYXRpb24iIC8+DQoNCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0JvIiAgIEFsaWFzPSLmsrnkvZPnp6/ns7vmlbAiIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBvaWwgZm9ybWF0aW9uIHZvbHVtZSBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9NaXVvIiBBbGlhcz0i5rK555u46buP5bqmIiAgIFVuaXQ9Im1QYS5zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjFlLTMwIiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBvaWwgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ28iICAgQWxpYXM9IuayueWOi+e8qeezu+aVsCIgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgb2lsIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0J3IiAgIEFsaWFzPSLmsLTkvZPnp6/ns7vmlbAiIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3YXRlciBmb3JtYXRpb24gdm9sdW1lIGZhY3RvciIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X01pdXciIEFsaWFzPSLmsLTnm7jpu4/luqYiICAgVW5pdD0ibVBhLnMiIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMWUtMzAiIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHdhdGVyIHZpc2Nvc2l0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0N3IiAgIEFsaWFzPSLmsLTljovnvKnns7vmlbAiIFVuaXQ9IjEvTVBhIiBEZWZhdWx0PSIwIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgIERpZ2l0PSIxMCIgU2NpZW50aWZpYz0iMSIgRGVzYz0icmVzdWx0IHdhdGVyIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0JnIiAgIEFsaWFzPSLmsJTkvZPnp6/ns7vmlbAiIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgZm9ybWF0aW9uIHZvbHVtZSBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9NaXVnIiBBbGlhcz0i5rCU55u46buP5bqmIiAgIFVuaXQ9Im1QYS5zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjFlLTMwIiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ2ciICAgQWxpYXM9IuawlOWOi+e8qeezu+aVsCIgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgZ2FzIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCjwvUm9vdD4NCg==
\ No newline at end of file
+PD94bWwgdmVycup_mpac2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCg0KPCEtLSoqKioqKioqKioqKiwzyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiotLT4NCjwhLS0gICAg5pys5paH5qGj55qE55uu55qE5piv5bu656uLV1RBSeaVsOWAvOinoyhOTSnmqKHlnZfnmoTlj4LmlbDlrprkuYkgICAgICAgICAgICAgICAgLS0+DQo8IS0tICAgIOS7heWMheWQq1BFQknmsYLop6PlmajmiYDpnIDnmoTlgqjlsYLjgIHkupXlkozlh6DkvZXlr7nosaHlj4LmlbAgICAgICAgICAgICAgICAgICAtLT4NCjwhLS0gICAgRGF0ZTogMjAyNjA2MTUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPg0KPCEtLSoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiotLT4NCg0KPCEtLQ0KICAgIOWPguaVsOiMg+WbtO+8mlBFQknmsYLop6PlmajmiYDpnIDnmoTlgqjlsYLjgIHkupXlkozlh6DkvZXlr7nosaHlj4LmlbANCiAgICDlr7nlupQgSFhfTldUTV9NT0RFTF9JTlBVVCDnu5PmnoTkvZPlrZfmrrUNCiAgICDpgJrov4cgX3BhcmFIZWxwZXItPmxvYWRQYXJhRGVmaW5lcygpIOWKoOi9ve+8jHNLZXkg5qCH6K+G5Li6IHNfTm1fU2VyaWUNCi0tPg0KDQo8Um9vdD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IuWCqOWxguWPguaVsCI+DQogICAgPFBhcmFJdGVtIE5hbWU9ImgiICAgQWxpYXM9IuWCqOWxguWOmuW6piIgICAgICAgVW5pdD0ibSIgICAgIERlZmF1bHQ9IjEwIiAgICBNYXg9IjEwMDAwIiAgTWluPSIxZS0wNSIgIERpZ2l0PSI2IiAgRGVzYz0icmVzZXJ2b2lyIHRoaWNrbmVzcyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUGkiICBBbGlhcz0i5Yid5aeL5Y6L5YqbIiAgICAgICBVbml0PSJNUGEiICAgRGVmYXVsdD0iNDAiICAgIE1heD0iNTAwIiAgICBNaW49IjAuMSIgICAgRGlnaXQ9IjYiICBEZXNjPSJpbml0aWFsIGZvcm1hdGlvbiBwcmVzc3VyZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iSyIgICBBbGlhcz0i5riX6YCP546HIiAgICAgICAgIFVuaXQ9IkRhcmN5IiBEZWZhdWx0PSIwLjAwMSIgTWF4PSIxMCIgICAgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InBlcm1lYWJpbGl0eSIgLz4KICAgIDxQYXJhSXRlbSBOYW1lPSJwaGkiIEFsaWFzPSLlrZTpmpnluqYiICAgICAgICAgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuMSIgICBNYXg9IjEiICAgICAgTWluPSIxZS0wNSIgIERpZ2l0PSI2IiAgRGVzYz0icG9yb3NpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkN0aSIgQWxpYXM9Iue7vOWQiOWOi+e8qeezu+aVsCIgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMC4wMDEiIE1heD0iMTAiICAgICBNaW49IjFlLTMwIiAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJ0b3RhbCBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkNmIiAgQWxpYXM9IuWyqeefs+WOi+e8qeezu+aVsCIgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMC4wMDAxIiBNYXg9IjEwIiAgICBNaW49IjFlLTMwIiAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyb2NrIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iU29pIiBBbGlhcz0i5Yid5aeL5ZCr5rK56aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMC44IiAgIE1heD0iMSIgICAgICBNaW49IjAiICAgICAgRGlnaXQ9IjYiICBEZXNjPSJpbml0aWFsIG9pbCBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJTd2kiIEFsaWFzPSLliJ3lp4vlkKvmsLTppbHlkozluqYiIFVuaXQ9IiIgICAgICBEZWZhdWx0PSIwLjIiICAgTWF4PSIxIiAgICAgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9ImluaXRpYWwgd2F0ZXIgc2F0dXJhdGlvbiIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSLkupXlj4LmlbAiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1giICAgIEFsaWFzPSJY5Z2Q5qCHIiAgICAgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiAgICAgTWF4PSIxMDAwMCIgIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgIERlc2M9IndlbGwgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1kiICAgIEFsaWFzPSJZ5Z2Q5qCHIiAgICAgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiAgICAgTWF4PSIxMDAwMCIgIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgIERlc2M9IndlbGwgWSBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1J3IiAgIEFsaWFzPSLkupXnrZLljYrlvoQiICAgIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjAuMSIgICBNYXg9IjEwIiAgICAgTWluPSIwLjAwMSIgIERpZ2l0PSI2IiAgRGVzYz0id2VsbGJvcmUgcmFkaXVzIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1NraW4iIEFsaWFzPSLooajnmq7ns7vmlbAiICAgIFVuaXQ9IiIgICAgIERlZmF1bHQ9IjAiICAgICBNYXg9IjEwMDAiICAgTWluPSItMTAwIiAgIERpZ2l0PSI2IiAgRGVzYz0ic2tpbiBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfQyIgICAgQWxpYXM9IuS6leetkuWCqOmbhuezu+aVsCIgVW5pdD0ibV4zL01QYSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwIiAgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9IndlbGxib3JlIHN0b3JhZ2UgY29lZmZpY2llbnQiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfV2VsbExlbmd0aCIgICAgICAgICBBbGlhcz0i5rC05bmz5q616ZW/5bqmIiAgIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjIwMCIgIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0RyYWluQW5nbGUiICAgICAgICAgQWxpYXM9IuaOkuawtOinkuW6piIgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjAiICAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgZHJhaW4gYW5nbGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfTnVtYmVyT2ZGcmFjdHVyZXMiICBBbGlhcz0i6KOC57yd5pWw6YePIiAgICAgVW5pdD0iIiAgICAgRGVmYXVsdD0iMTUiICAgTWF4PSIxMDAwIiAgTWluPSIyIiAgICAgRGlnaXQ9IjAiIERlc2M9Im51bWJlciBvZiBmcmFjdHVyZXMiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfRnJhY3R1cmVIYWxmTGVuZ3RoIiBBbGlhcz0i6KOC57yd5Y2K6ZW/IiAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iNTAiICAgTWF4PSIxMDAwMCIgTWluPSIxZS0wNSIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGhhbGYtbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0ZyYWN0dXJlQW5nbGUiICAgICAgQWxpYXM9Iuijgue8neinkuW6piIgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjkwIiAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBhbmdsZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19EZmMiICAgICAgICAgICAgICAgIEFsaWFzPSLoo4LnvJ3lr7zmtYHog73lipsiIFVuaXQ9Im1kLm0iIERlZmF1bHQ9IjEwMDAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBjb25kdWN0aXZpdHkiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0i54us56uL6KOC57yd5Y+C5pWwIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRl9YMCIgQWxpYXM9Iui1t+eCuVgwIiAgIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgc3RhcnQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGX1kwIiBBbGlhcz0i6LW354K5WTAiICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBzdGFydCBZIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfWDEiIEFsaWFzPSLnu4jngrlYMSIgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGVuZCBYIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfWTEiIEFsaWFzPSLnu4jngrlZMSIgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGVuZCBZIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfRkMiIEFsaWFzPSLlr7zmtYHog73lipsiIFVuaXQ9Im1kLm0iIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgY29uZHVjdGl2aXR5IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IuaWreWxguWPguaVsCI+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZUX1gwIiBBbGlhcz0i6LW354K5WDAiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZmF1bHQgc3RhcnQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9ZMCIgQWxpYXM9Iui1t+eCuVkwIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZhdWx0IHN0YXJ0IFkgY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRlRfWDEiIEFsaWFzPSLnu4jngrlYMSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBlbmQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9ZMSIgQWxpYXM9Iue7iOeCuVkxIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZhdWx0IGVuZCBZIGNvb3JkaW5hdGUiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0i55+p5b2i6L6555WM5Y+C5pWwIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1pbiIgQWxpYXM9IuW3pui+ueeVjFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1pbmltdW0gWCIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWU1pbiIgQWxpYXM9IuS4i+i+ueeVjFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1pbmltdW0gWSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1heCIgQWxpYXM9IuWPs+i+ueeVjFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1heGltdW0gWCIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWU1heCIgQWxpYXM9IuS4iui+ueeVjFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1heGltdW0gWSIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSLlnIblvaLovrnnlYzlj4LmlbAiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19DZW50ZXJYIiBBbGlhcz0i5ZyG5b+DWCIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJjaXJjbGUgYm91bmRhcnkgY2VudGVyIFgiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkJDX0NlbnRlclkiIEFsaWFzPSLlnIblv4NZIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImNpcmNsZSBib3VuZGFyeSBjZW50ZXIgWSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQkNfUmFkaXVzIiAgQWxpYXM9IuWNiuW+hCIgIFVuaXQ9Im0iIERlZmF1bHQ9IjEiIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiICBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IHJhZGl1cyIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSLlpJrovrnlvaLovrnnlYzngrnlj4LmlbAiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUF9YIiBBbGlhcz0iWOWdkOaghyIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJwb2x5Z29uIGJvdW5kYXJ5IHZlcnRleCBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUF9ZIiBBbGlhcz0iWeWdkOaghyIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJwb2x5Z29uIGJvdW5kYXJ5IHZlcnRleCBZIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IuWMuuWfn+WPguaVsCI+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJfTGVha2FnZSIgQWxpYXM9IuazhOmcsuezu+aVsCIgVW5pdD0iIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiIERpZ2l0PSI2IiBEZXNjPSJyZWdpb24gbGVha2FnZSBjb2VmZmljaWVudCIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSLmoIforrDljLrln5/lj4LmlbAiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSTV9Db21XIiAgICAgICBBbGlhcz0i5YKo6IO95q+UIiBVbml0PSIiIERlZmF1bHQ9IjEiIE1heD0iMSIgICAgTWluPSIwIiAgICAgRGlnaXQ9IjYiIERlc2M9InN0b3JhZ2UgcmF0aW8iIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJNX0NvbUtyIiAgICAgIEFsaWFzPSLmtYHluqbmr5QiIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxMDAwIiBNaW49IjAuMDAxIiBEaWdpdD0iNiIgRGVzYz0ibW9iaWxpdHkgcmF0aW8iIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJNX05ldFRvR3Jvc3MiIEFsaWFzPSLlh4Dmr5vmr5QiIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxIiAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgRGVzYz0ibmV0LXRvLWdyb3NzIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8IS0tIOe7k+aenOmdouadv+WPguaVsOWPqueUsSByZWJ1aWxkUmVzdWx0UGFyYXMoKSDpgInmi6nvvIzkuI3kvJrlh7rnjrDlnKjlt6bkvqfpnaLmnb/kuK3jgIIgLS0+DQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0i57uT5p6c5Y+C5pWwIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1dfUnciICAgQWxpYXM9IuS6leW+hCIgICAgICAgICBVbml0PSJtIiAgICAgIERlZmF1bHQ9IjAuMSIgICAgTWF4PSIxMCIgICAgTWluPSIwLjAwMSIgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHdlbGxib3JlIHJhZGl1cyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1dfRGZjIiAgQWxpYXM9Iuijgue8neWvvOa1geiDveWKmyIgVW5pdD0ibWQubSIgICBEZWZhdWx0PSIxMDAwIiAgIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBmcmFjdHVyZSBjb25kdWN0aXZpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX1NraW4iIEFsaWFzPSLooajnmq7ns7vmlbAiICAgICBVbml0PSIiICAgICAgIERlZmF1bHQ9IjAiICAgICAgTWF4PSIxMDAwIiAgTWluPSItMTAwIiAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHNraW4gZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfV19DIiAgICBBbGlhcz0i5LqV562S5YKo6ZuG57O75pWwIiBVbml0PSJtXjMvTVBhIiBEZWZhdWx0PSIwIiAgICAgIE1heD0iMTAwMCIgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3ZWxsYm9yZSBzdG9yYWdlIGNvZWZmaWNpZW50IiAvPg0KDQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9QaSIgIEFsaWFzPSLliJ3lp4vlnLDlsYLljovlipsiICAgVW5pdD0iTVBhIiAgIERlZmF1bHQ9IjQwIiAgICAgTWF4PSI1MDAiICAgTWluPSIwLjEiICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgaW5pdGlhbCBmb3JtYXRpb24gcHJlc3N1cmUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9LIiAgIEFsaWFzPSLmuJfpgI/njociICAgICAgICAgVW5pdD0iRGFyY3kiIERlZmF1bHQ9IjAuMDAxIiAgTWF4PSIxMCIgICAgTWluPSIwIiAgICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgcGVybWVhYmlsaXR5IiAvPgogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9oIiAgIEFsaWFzPSLlgqjlsYLljprluqYiICAgICAgIFVuaXQ9Im0iICAgICBEZWZhdWx0PSIxMCIgICAgIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHJlc2Vydm9pciB0aGlja25lc3MiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9waGkiIEFsaWFzPSLlrZTpmpnluqYiICAgICAgICAgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuMSIgICAgTWF4PSIxIiAgICAgTWluPSIxZS0wNSIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgcG9yb3NpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9DdGkiIEFsaWFzPSLnu7zlkIjljovnvKnns7vmlbAiICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAuMDAxIiAgTWF4PSIxMCIgICAgTWluPSIxZS0zMCIgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgdG90YWwgY29tcHJlc3NpYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ2YiICBBbGlhcz0i5bKp55+z5Y6L57yp57O75pWwIiAgIFVuaXQ9IjEvTVBhIiBEZWZhdWx0PSIwLjAwMDEiIE1heD0iMTAiICAgIE1pbj0iMWUtMzAiIERpZ2l0PSIxMCIgU2NpZW50aWZpYz0iMSIgRGVzYz0icmVzdWx0IHJvY2sgY29tcHJlc3NpYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU29pIiBBbGlhcz0i5Yid5aeL5ZCr5rK56aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMC44IiAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIG9pbCBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU2dpIiBBbGlhcz0i5Yid5aeL5ZCr5rCU6aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMCIgICAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIGdhcyBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU3dpIiBBbGlhcz0i5Yid5aeL5ZCr5rC06aWx5ZKM5bqmIiBVbml0PSIiICAgICAgRGVmYXVsdD0iMC4yIiAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIHdhdGVyIHNhdHVyYXRpb24iIC8+DQoNCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0JvIiAgIEFsaWFzPSLmsrnkvZPnp6/ns7vmlbAiIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBvaWwgZm9ybWF0aW9uIHZvbHVtZSBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9NaXVvIiBBbGlhcz0i5rK555u46buP5bqmIiAgIFVuaXQ9Im1QYS5zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjFlLTMwIiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBvaWwgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ28iICAgQWxpYXM9IuayueWOi+e8qeezu+aVsCIgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgb2lsIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0J3IiAgIEFsaWFzPSLmsLTkvZPnp6/ns7vmlbAiIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3YXRlciBmb3JtYXRpb24gdm9sdW1lIGZhY3RvciIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X01pdXciIEFsaWFzPSLmsLTnm7jpu4/luqYiICAgVW5pdD0ibVBhLnMiIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMWUtMzAiIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHdhdGVyIHZpc2Nvc2l0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0N3IiAgIEFsaWFzPSLmsLTljovnvKnns7vmlbAiIFVuaXQ9IjEvTVBhIiBEZWZhdWx0PSIwIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgIERpZ2l0PSIxMCIgU2NpZW50aWZpYz0iMSIgRGVzYz0icmVzdWx0IHdhdGVyIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0JnIiAgIEFsaWFzPSLmsJTkvZPnp6/ns7vmlbAiIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgZm9ybWF0aW9uIHZvbHVtZSBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9NaXVnIiBBbGlhcz0i5rCU55u46buP5bqmIiAgIFVuaXQ9Im1QYS5zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjFlLTMwIiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ2ciICAgQWxpYXM9IuawlOWOi+e8qeezu+aVsCIgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgZ2FzIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCjwvUm9vdD4NCg==
\ No newline at end of file
diff --git a/Bin/Config/Common/ModelParaDefinesNM_en.dat b/Bin/Config/Common/ModelParaDefinesNM_en.dat
index a1226c0..cac7ac4 100644
--- a/Bin/Config/Common/ModelParaDefinesNM_en.dat
+++ b/Bin/Config/Common/ModelParaDefinesNM_en.dat
@@ -1 +1 @@
-PD94bWwgdmVycup_mpac2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCg0KPCEtLSoqKioqKioqKioqKiwzyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiotLT4NCjwhLS0gICAgVGhpcyBmaWxlIGRlZmluZXMgcGFyYW1ldGVycyBmb3IgdGhlIFdUQUkgTnVtZXJpY2FsIChOTSkgICAgLS0+DQo8IS0tICAgIG1vZHVsZS4gT25seSByZXNlcnZvaXIsIHdlbGwsIGFuZCBnZW9tZXRyeSBwYXJhbWV0ZXJzICAgICAgICAtLT4NCjwhLS0gICAgcmVxdWlyZWQgYnkgdGhlIFBFQkkgc29sdmVyIGFyZSBpbmNsdWRlZC4gICAgICAgICAgICAgICAgICAgIC0tPg0KPCEtLSAgICBEYXRlOiAyMDI2MDYxNSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+DQo8IS0tKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi0tPg0KDQo8IS0tDQogICAgUGFyYW1ldGVyIHNjb3BlOiByZXNlcnZvaXIsIHdlbGwsIGFuZCBnZW9tZXRyeSBwYXJhbWV0ZXJzIHJlcXVpcmVkIGJ5IHRoZSBQRUJJIHNvbHZlcg0KICAgIENvcnJlc3BvbmRzIHRvIEhYX05XVE1fTU9ERUxfSU5QVVQgc3RydWN0IGZpZWxkcw0KICAgIExvYWRlZCB2aWEgX3BhcmFIZWxwZXItPmxvYWRQYXJhRGVmaW5lcygpLCBzS2V5ID0gc19ObV9TZXJpZQ0KLS0+DQoNCjxSb290Pg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0iUmVzZXJ2b2lyIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJoIiAgIEFsaWFzPSJSZXNlcnZvaXIgVGhpY2tuZXNzIiAgICAgICAgIFVuaXQ9Im0iICAgICBEZWZhdWx0PSIxMCIgICAgTWF4PSIxMDAwMCIgIE1pbj0iMWUtMDUiICBEaWdpdD0iMyIgIERlc2M9InJlc2Vydm9pciB0aGlja25lc3MiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlBpIiAgQWxpYXM9IkluaXRpYWwgUHJlc3N1cmUiICAgICAgICAgICAgVW5pdD0iTVBhIiAgIERlZmF1bHQ9IjQwIiAgICBNYXg9IjUwMCIgICAgTWluPSIwLjEiICAgIERpZ2l0PSI1IiAgRGVzYz0iaW5pdGlhbCBmb3JtYXRpb24gcHJlc3N1cmUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IksiICAgQWxpYXM9IlBlcm1lYWJpbGl0eSIgICAgICAgICAgICAgICAgVW5pdD0ibUQiICAgIERlZmF1bHQ9IjEiICAgICBNYXg9IjEwMDAwIiAgTWluPSIwIiAgICAgIERpZ2l0PSI2IiAgRGVzYz0icGVybWVhYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJwaGkiIEFsaWFzPSJQb3Jvc2l0eSIgICAgICAgICAgICAgICAgICAgIFVuaXQ9IiIgICAgICBEZWZhdWx0PSIwLjEiICAgTWF4PSIxIiAgICAgIE1pbj0iMWUtMDUiICBEaWdpdD0iNCIgIERlc2M9InBvcm9zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJDdGkiIEFsaWFzPSJUb3RhbCBDb21wcmVzc2liaWxpdHkiICAgICAgIFVuaXQ9IjEvTVBhIiBEZWZhdWx0PSIwLjAwMSIgTWF4PSIxMCIgICAgIE1pbj0iMWUtMzAiICBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InRvdGFsIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQ2YiICBBbGlhcz0iUm9jayBDb21wcmVzc2liaWxpdHkiICAgICAgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMC4wMDAxIiBNYXg9IjEwIiAgICBNaW49IjFlLTMwIiAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyb2NrIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iU29pIiBBbGlhcz0iSW5pdGlhbCBPaWwgU2F0dXJhdGlvbiIgICAgICBVbml0PSIiICAgICAgRGVmYXVsdD0iMC44IiAgIE1heD0iMSIgICAgICBNaW49IjAiICAgICAgRGlnaXQ9IjYiICBEZXNjPSJpbml0aWFsIG9pbCBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJTd2kiIEFsaWFzPSJJbml0aWFsIFdhdGVyIFNhdHVyYXRpb24iICAgIFVuaXQ9IiIgICAgICBEZWZhdWx0PSIwLjIiICAgTWF4PSIxIiAgICAgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9ImluaXRpYWwgd2F0ZXIgc2F0dXJhdGlvbiIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSJXZWxsIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1giICAgIEFsaWFzPSJYIENvb3JkaW5hdGUiICAgICAgICAgICAgVW5pdD0ibSIgICAgICBEZWZhdWx0PSIwIiAgICAgTWF4PSIxMDAwMCIgIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgIERlc2M9IndlbGwgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1kiICAgIEFsaWFzPSJZIENvb3JkaW5hdGUiICAgICAgICAgICAgVW5pdD0ibSIgICAgICBEZWZhdWx0PSIwIiAgICAgTWF4PSIxMDAwMCIgIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgIERlc2M9IndlbGwgWSBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1J3IiAgIEFsaWFzPSJXZWxsYm9yZSBSYWRpdXMiICAgICAgICAgVW5pdD0ibSIgICAgICBEZWZhdWx0PSIwLjEiICAgTWF4PSIxMCIgICAgIE1pbj0iMC4wMDEiICBEaWdpdD0iNiIgIERlc2M9IndlbGxib3JlIHJhZGl1cyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19Ta2luIiBBbGlhcz0iU2tpbiBGYWN0b3IiICAgICAgICAgICAgIFVuaXQ9IiIgICAgICAgRGVmYXVsdD0iMCIgICAgIE1heD0iMTAwMCIgICBNaW49Ii0xMDAiICAgRGlnaXQ9IjYiICBEZXNjPSJza2luIGZhY3RvciIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19DIiAgICBBbGlhcz0iV2VsbGJvcmUgU3RvcmFnZSBDb2VmZiIgIFVuaXQ9Im1eMy9NUGEiIERlZmF1bHQ9IjAiICAgICBNYXg9IjEwMDAiICAgTWluPSIwIiAgICAgIERpZ2l0PSI2IiAgRGVzYz0id2VsbGJvcmUgc3RvcmFnZSBjb2VmZmljaWVudCIgLz4KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1dlbGxMZW5ndGgiICAgICAgICAgQWxpYXM9Ikhvcml6b250YWwgV2VsbCBMZW5ndGgiIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjIwMCIgIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0RyYWluQW5nbGUiICAgICAgICAgQWxpYXM9IkRyYWluIEFuZ2xlIiAgICAgICAgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjAiICAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgZHJhaW4gYW5nbGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfTnVtYmVyT2ZGcmFjdHVyZXMiICBBbGlhcz0iTnVtYmVyIG9mIEZyYWN0dXJlcyIgICAgVW5pdD0iIiAgICAgRGVmYXVsdD0iMTUiICAgTWF4PSIxMDAwIiAgTWluPSIyIiAgICAgRGlnaXQ9IjAiIERlc2M9Im51bWJlciBvZiBmcmFjdHVyZXMiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfRnJhY3R1cmVIYWxmTGVuZ3RoIiBBbGlhcz0iRnJhY3R1cmUgSGFsZi1MZW5ndGgiICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iNTAiICAgTWF4PSIxMDAwMCIgTWluPSIxZS0wNSIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGhhbGYtbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0ZyYWN0dXJlQW5nbGUiICAgICAgQWxpYXM9IkZyYWN0dXJlIEFuZ2xlIiAgICAgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjkwIiAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBhbmdsZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19EZmMiICAgICAgICAgICAgICAgIEFsaWFzPSJGcmFjdHVyZSBDb25kdWN0aXZpdHkiICBVbml0PSJtZC5tIiBEZWZhdWx0PSIxMDAwIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgY29uZHVjdGl2aXR5IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IkluZGVwZW5kZW50IEZyYWN0dXJlIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGX1gwIiBBbGlhcz0iU3RhcnQgWDAiICAgICAgICAgICAgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIHN0YXJ0IFggY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRl9ZMCIgQWxpYXM9IlN0YXJ0IFkwIiAgICAgICAgICAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBzdGFydCBZIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfWDEiIEFsaWFzPSJFbmQgWDEiICAgICAgICAgICAgICAgIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgZW5kIFggY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRl9ZMSIgQWxpYXM9IkVuZCBZMSIgICAgICAgICAgICAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBlbmQgWSBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGX0ZDIiBBbGlhcz0iRnJhY3R1cmUgQ29uZHVjdGl2aXR5IiBVbml0PSJtZC5tIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICAgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGNvbmR1Y3Rpdml0eSIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSJGYXVsdCBQYXJhbWV0ZXJzIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRlRfWDAiIEFsaWFzPSJTdGFydCBYMCIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBzdGFydCBYIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZUX1kwIiBBbGlhcz0iU3RhcnQgWTAiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZmF1bHQgc3RhcnQgWSBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9YMSIgQWxpYXM9IkVuZCBYMSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBlbmQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9ZMSIgQWxpYXM9IkVuZCBZMSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBlbmQgWSBjb29yZGluYXRlIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IlJlY3RhbmdsZSBCb3VuZGFyeSBQYXJhbWV0ZXJzIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1pbiIgQWxpYXM9IkxlZnQgQm91bmRhcnkgWCIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJyZWN0YW5nbGUgYm91bmRhcnkgbWluaW11bSBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUl9ZTWluIiBBbGlhcz0iQm90dG9tIEJvdW5kYXJ5IFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1pbmltdW0gWSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1heCIgQWxpYXM9IlJpZ2h0IEJvdW5kYXJ5IFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1heGltdW0gWCIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWU1heCIgQWxpYXM9IlRvcCBCb3VuZGFyeSBZIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9InJlY3RhbmdsZSBib3VuZGFyeSBtYXhpbXVtIFkiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0iQ2lyY2xlIEJvdW5kYXJ5IFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19DZW50ZXJYIiBBbGlhcz0iQ2VudGVyIFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IGNlbnRlciBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19DZW50ZXJZIiBBbGlhcz0iQ2VudGVyIFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IGNlbnRlciBZIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19SYWRpdXMiICBBbGlhcz0iUmFkaXVzIiAgIFVuaXQ9Im0iIERlZmF1bHQ9IjEiIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiICBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IHJhZGl1cyIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSJQb2x5Z29uIEJvdW5kYXJ5IFZlcnRleCBQYXJhbWV0ZXJzIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlBfWCIgQWxpYXM9IlggQ29vcmRpbmF0ZSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJwb2x5Z29uIGJvdW5kYXJ5IHZlcnRleCBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUF9ZIiBBbGlhcz0iWSBDb29yZGluYXRlIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9InBvbHlnb24gYm91bmRhcnkgdmVydGV4IFkiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0iUmVnaW9uIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSX0xlYWthZ2UiIEFsaWFzPSJMZWFrYWdlIENvZWZmaWNpZW50IiBVbml0PSIiIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMCIgRGlnaXQ9IjYiIERlc2M9InJlZ2lvbiBsZWFrYWdlIGNvZWZmaWNpZW50IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IlJlZ2lvbiBNYXJrIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSTV9Db21XIiAgICAgICBBbGlhcz0iU3RvcmFnZSBSYXRpbyIgVW5pdD0iIiBEZWZhdWx0PSIxIiBNYXg9IjEiICAgIE1pbj0iMCIgICAgIERpZ2l0PSI2IiBEZXNjPSJzdG9yYWdlIHJhdGlvIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSTV9Db21LciIgICAgICBBbGlhcz0iTW9iaWxpdHkgUmF0aW8iIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxMDAwIiBNaW49IjAuMDAxIiBEaWdpdD0iNiIgRGVzYz0ibW9iaWxpdHkgcmF0aW8iIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJNX05ldFRvR3Jvc3MiIEFsaWFzPSJOZXQtdG8tR3Jvc3MiIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxIiAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgRGVzYz0ibmV0LXRvLWdyb3NzIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8IS0tIFJlc3VsdCBwYXJhbWV0ZXJzIGFyZSBzZWxlY3RlZCBvbmx5IGJ5IHJlYnVpbGRSZXN1bHRQYXJhcygpLiAtLT4NCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSJSZXN1bHQgUGFyYW1ldGVycyI+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX1J3IiAgIEFsaWFzPSJXZWxsIFJhZGl1cyIgICAgICAgICAgICAgVW5pdD0ibSIgICAgICBEZWZhdWx0PSIwLjEiICAgIE1heD0iMTAiICAgIE1pbj0iMC4wMDEiICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3ZWxsYm9yZSByYWRpdXMiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX0RmYyIgIEFsaWFzPSJGcmFjdHVyZSBDb25kdWN0aXZpdHkiICBVbml0PSJtZC5tIiAgIERlZmF1bHQ9IjEwMDAiICAgTWF4PSIxZSszMCIgTWluPSIwIiAgICAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IGZyYWN0dXJlIGNvbmR1Y3Rpdml0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1dfU2tpbiIgQWxpYXM9IlNraW4gRmFjdG9yIiAgICAgICAgICAgIFVuaXQ9IiIgICAgICAgRGVmYXVsdD0iMCIgICAgICBNYXg9IjEwMDAiICBNaW49Ii0xMDAiICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgc2tpbiBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX0MiICAgIEFsaWFzPSJXZWxsYm9yZSBTdG9yYWdlIENvZWZmIiBVbml0PSJtXjMvTVBhIiBEZWZhdWx0PSIwIiAgICAgIE1heD0iMTAwMCIgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3ZWxsYm9yZSBzdG9yYWdlIGNvZWZmaWNpZW50IiAvPgoNCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1BpIiAgQWxpYXM9IkluaXRpYWwgRm9ybWF0aW9uIFByZXNzdXJlIiBVbml0PSJNUGEiICAgRGVmYXVsdD0iNDAiICAgICBNYXg9IjUwMCIgICBNaW49IjAuMSIgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIGZvcm1hdGlvbiBwcmVzc3VyZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0siICAgQWxpYXM9IlBlcm1lYWJpbGl0eSIgICAgICAgICAgICAgICBVbml0PSJtRCIgICAgRGVmYXVsdD0iMSIgICAgICBNYXg9IjEwMDAwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBwZXJtZWFiaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9oIiAgIEFsaWFzPSJSZXNlcnZvaXIgVGhpY2tuZXNzIiAgICAgICAgVW5pdD0ibSIgICAgIERlZmF1bHQ9IjEwIiAgICAgTWF4PSIxMDAwMCIgTWluPSIxZS0wNSIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgcmVzZXJ2b2lyIHRoaWNrbmVzcyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X3BoaSIgQWxpYXM9IlBvcm9zaXR5IiAgICAgICAgICAgICAgICAgICBVbml0PSIiICAgICAgRGVmYXVsdD0iMC4xIiAgICBNYXg9IjEiICAgICBNaW49IjFlLTA1IiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBwb3Jvc2l0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0N0aSIgQWxpYXM9IlRvdGFsIENvbXByZXNzaWJpbGl0eSIgICAgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMC4wMDEiICBNYXg9IjEwIiAgICBNaW49IjFlLTMwIiBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InJlc3VsdCB0b3RhbCBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9DZiIgIEFsaWFzPSJSb2NrIENvbXByZXNzaWJpbGl0eSIgICAgICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAuMDAwMSIgTWF4PSIxMCIgICAgTWluPSIxZS0zMCIgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgcm9jayBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9Tb2kiIEFsaWFzPSJJbml0aWFsIE9pbCBTYXR1cmF0aW9uIiAgICAgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuOCIgICAgTWF4PSIxIiAgICAgTWluPSIwIiAgICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgaW5pdGlhbCBvaWwgc2F0dXJhdGlvbiIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1NnaSIgQWxpYXM9IkluaXRpYWwgR2FzIFNhdHVyYXRpb24iICAgICBVbml0PSIiICAgICAgRGVmYXVsdD0iMCIgICAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIGdhcyBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU3dpIiBBbGlhcz0iSW5pdGlhbCBXYXRlciBTYXR1cmF0aW9uIiAgIFVuaXQ9IiIgICAgICBEZWZhdWx0PSIwLjIiICAgIE1heD0iMSIgICAgIE1pbj0iMCIgICAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IGluaXRpYWwgd2F0ZXIgc2F0dXJhdGlvbiIgLz4NCg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQm8iICAgQWxpYXM9Ik9pbCBGb3JtYXRpb24gVm9sdW1lIEZhY3RvciIgICBVbml0PSJtMy9tMyIgRGVmYXVsdD0iMSIgTWF4PSIxZSszMCIgTWluPSIwIiAgICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgb2lsIGZvcm1hdGlvbiB2b2x1bWUgZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfTWl1byIgQWxpYXM9Ik9pbCBWaXNjb3NpdHkiICAgICAgICAgICAgICAgICBVbml0PSJtUGEucyIgRGVmYXVsdD0iMSIgTWF4PSIxZSszMCIgTWluPSIxZS0zMCIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgb2lsIHZpc2Nvc2l0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0NvIiAgIEFsaWFzPSJPaWwgQ29tcHJlc3NpYmlsaXR5IiAgICAgICAgICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgb2lsIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0J3IiAgIEFsaWFzPSJXYXRlciBGb3JtYXRpb24gVm9sdW1lIEZhY3RvciIgVW5pdD0ibTMvbTMiIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHdhdGVyIGZvcm1hdGlvbiB2b2x1bWUgZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfTWl1dyIgQWxpYXM9IldhdGVyIFZpc2Nvc2l0eSIgICAgICAgICAgICAgICBVbml0PSJtUGEucyIgRGVmYXVsdD0iMSIgTWF4PSIxZSszMCIgTWluPSIxZS0zMCIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgd2F0ZXIgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ3ciICAgQWxpYXM9IldhdGVyIENvbXByZXNzaWJpbGl0eSIgICAgICAgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMCIgTWF4PSIxZSszMCIgTWluPSIwIiAgICBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InJlc3VsdCB3YXRlciBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9CZyIgICBBbGlhcz0iR2FzIEZvcm1hdGlvbiBWb2x1bWUgRmFjdG9yIiAgIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgZm9ybWF0aW9uIHZvbHVtZSBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9NaXVnIiBBbGlhcz0iR2FzIFZpc2Nvc2l0eSIgICAgICAgICAgICAgICAgIFVuaXQ9Im1QYS5zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjFlLTMwIiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ2ciICAgQWxpYXM9IkdhcyBDb21wcmVzc2liaWxpdHkiICAgICAgICAgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMCIgTWF4PSIxZSszMCIgTWluPSIwIiAgICBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InJlc3VsdCBnYXMgY29tcHJlc3NpYmlsaXR5IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KPC9Sb290Pg0K
\ No newline at end of file
+PD94bWwgdmVycup_mpac2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCg0KPCEtLSoqKioqKioqKioqKiwzyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiotLT4NCjwhLS0gICAgVGhpcyBmaWxlIGRlZmluZXMgcGFyYW1ldGVycyBmb3IgdGhlIFdUQUkgTnVtZXJpY2FsIChOTSkgICAgLS0+DQo8IS0tICAgIG1vZHVsZS4gT25seSByZXNlcnZvaXIsIHdlbGwsIGFuZCBnZW9tZXRyeSBwYXJhbWV0ZXJzICAgICAgICAtLT4NCjwhLS0gICAgcmVxdWlyZWQgYnkgdGhlIFBFQkkgc29sdmVyIGFyZSBpbmNsdWRlZC4gICAgICAgICAgICAgICAgICAgIC0tPg0KPCEtLSAgICBEYXRlOiAyMDI2MDYxNSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+DQo8IS0tKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi0tPg0KDQo8IS0tDQogICAgUGFyYW1ldGVyIHNjb3BlOiByZXNlcnZvaXIsIHdlbGwsIGFuZCBnZW9tZXRyeSBwYXJhbWV0ZXJzIHJlcXVpcmVkIGJ5IHRoZSBQRUJJIHNvbHZlcg0KICAgIENvcnJlc3BvbmRzIHRvIEhYX05XVE1fTU9ERUxfSU5QVVQgc3RydWN0IGZpZWxkcw0KICAgIExvYWRlZCB2aWEgX3BhcmFIZWxwZXItPmxvYWRQYXJhRGVmaW5lcygpLCBzS2V5ID0gc19ObV9TZXJpZQ0KLS0+DQoNCjxSb290Pg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0iUmVzZXJ2b2lyIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJoIiAgIEFsaWFzPSJSZXNlcnZvaXIgVGhpY2tuZXNzIiAgICAgICAgIFVuaXQ9Im0iICAgICBEZWZhdWx0PSIxMCIgICAgTWF4PSIxMDAwMCIgIE1pbj0iMWUtMDUiICBEaWdpdD0iMyIgIERlc2M9InJlc2Vydm9pciB0aGlja25lc3MiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlBpIiAgQWxpYXM9IkluaXRpYWwgUHJlc3N1cmUiICAgICAgICAgICAgVW5pdD0iTVBhIiAgIERlZmF1bHQ9IjQwIiAgICBNYXg9IjUwMCIgICAgTWluPSIwLjEiICAgIERpZ2l0PSI1IiAgRGVzYz0iaW5pdGlhbCBmb3JtYXRpb24gcHJlc3N1cmUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IksiICAgQWxpYXM9IlBlcm1lYWJpbGl0eSIgICAgICAgICAgICAgICAgVW5pdD0iRGFyY3kiIERlZmF1bHQ9IjAuMDAxIiBNYXg9IjEwIiAgICAgTWluPSIwIiAgICAgIERpZ2l0PSI2IiAgRGVzYz0icGVybWVhYmlsaXR5IiAvPgogICAgPFBhcmFJdGVtIE5hbWU9InBoaSIgQWxpYXM9IlBvcm9zaXR5IiAgICAgICAgICAgICAgICAgICAgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuMSIgICBNYXg9IjEiICAgICAgTWluPSIxZS0wNSIgIERpZ2l0PSI0IiAgRGVzYz0icG9yb3NpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkN0aSIgQWxpYXM9IlRvdGFsIENvbXByZXNzaWJpbGl0eSIgICAgICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAuMDAxIiBNYXg9IjEwIiAgICAgTWluPSIxZS0zMCIgIERpZ2l0PSIxMCIgU2NpZW50aWZpYz0iMSIgRGVzYz0idG90YWwgY29tcHJlc3NpYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJDZiIgIEFsaWFzPSJSb2NrIENvbXByZXNzaWJpbGl0eSIgICAgICAgIFVuaXQ9IjEvTVBhIiBEZWZhdWx0PSIwLjAwMDEiIE1heD0iMTAiICAgIE1pbj0iMWUtMzAiICBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InJvY2sgY29tcHJlc3NpYmlsaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJTb2kiIEFsaWFzPSJJbml0aWFsIE9pbCBTYXR1cmF0aW9uIiAgICAgIFVuaXQ9IiIgICAgICBEZWZhdWx0PSIwLjgiICAgTWF4PSIxIiAgICAgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9ImluaXRpYWwgb2lsIHNhdHVyYXRpb24iIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlN3aSIgQWxpYXM9IkluaXRpYWwgV2F0ZXIgU2F0dXJhdGlvbiIgICAgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuMiIgICBNYXg9IjEiICAgICAgTWluPSIwIiAgICAgIERpZ2l0PSI2IiAgRGVzYz0iaW5pdGlhbCB3YXRlciBzYXR1cmF0aW9uIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IldlbGwgUGFyYW1ldGVycyI+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfWCIgICAgQWxpYXM9IlggQ29vcmRpbmF0ZSIgICAgICAgICAgICBVbml0PSJtIiAgICAgIERlZmF1bHQ9IjAiICAgICBNYXg9IjEwMDAwIiAgTWluPSItMTAwMDAiIERpZ2l0PSI2IiAgRGVzYz0id2VsbCBYIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfWSIgICAgQWxpYXM9IlkgQ29vcmRpbmF0ZSIgICAgICAgICAgICBVbml0PSJtIiAgICAgIERlZmF1bHQ9IjAiICAgICBNYXg9IjEwMDAwIiAgTWluPSItMTAwMDAiIERpZ2l0PSI2IiAgRGVzYz0id2VsbCBZIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfUnciICAgQWxpYXM9IldlbGxib3JlIFJhZGl1cyIgICAgICAgICBVbml0PSJtIiAgICAgIERlZmF1bHQ9IjAuMSIgICBNYXg9IjEwIiAgICAgTWluPSIwLjAwMSIgIERpZ2l0PSI2IiAgRGVzYz0id2VsbGJvcmUgcmFkaXVzIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1NraW4iIEFsaWFzPSJTa2luIEZhY3RvciIgICAgICAgICAgICAgVW5pdD0iIiAgICAgICBEZWZhdWx0PSIwIiAgICAgTWF4PSIxMDAwIiAgIE1pbj0iLTEwMCIgICBEaWdpdD0iNiIgIERlc2M9InNraW4gZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0MiICAgIEFsaWFzPSJXZWxsYm9yZSBTdG9yYWdlIENvZWZmIiAgVW5pdD0ibV4zL01QYSIgRGVmYXVsdD0iMCIgICAgIE1heD0iMTAwMCIgICBNaW49IjAiICAgICAgRGlnaXQ9IjYiICBEZXNjPSJ3ZWxsYm9yZSBzdG9yYWdlIGNvZWZmaWNpZW50IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX1dlbGxMZW5ndGgiICAgICAgICAgQWxpYXM9Ikhvcml6b250YWwgV2VsbCBMZW5ndGgiIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjIwMCIgIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0RyYWluQW5nbGUiICAgICAgICAgQWxpYXM9IkRyYWluIEFuZ2xlIiAgICAgICAgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjAiICAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJob3Jpem9udGFsIHdlbGwgZHJhaW4gYW5nbGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfTnVtYmVyT2ZGcmFjdHVyZXMiICBBbGlhcz0iTnVtYmVyIG9mIEZyYWN0dXJlcyIgICAgVW5pdD0iIiAgICAgRGVmYXVsdD0iMTUiICAgTWF4PSIxMDAwIiAgTWluPSIyIiAgICAgRGlnaXQ9IjAiIERlc2M9Im51bWJlciBvZiBmcmFjdHVyZXMiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IldfRnJhY3R1cmVIYWxmTGVuZ3RoIiBBbGlhcz0iRnJhY3R1cmUgSGFsZi1MZW5ndGgiICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iNTAiICAgTWF4PSIxMDAwMCIgTWluPSIxZS0wNSIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGhhbGYtbGVuZ3RoIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJXX0ZyYWN0dXJlQW5nbGUiICAgICAgQWxpYXM9IkZyYWN0dXJlIEFuZ2xlIiAgICAgICAgIFVuaXQ9Im8iICAgIERlZmF1bHQ9IjkwIiAgIE1heD0iMzYwIiAgIE1pbj0iLTM2MCIgIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBhbmdsZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iV19EZmMiICAgICAgICAgICAgICAgIEFsaWFzPSJGcmFjdHVyZSBDb25kdWN0aXZpdHkiICBVbml0PSJtZC5tIiBEZWZhdWx0PSIxMDAwIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgY29uZHVjdGl2aXR5IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IkluZGVwZW5kZW50IEZyYWN0dXJlIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGX1gwIiBBbGlhcz0iU3RhcnQgWDAiICAgICAgICAgICAgICBVbml0PSJtIiAgICBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIHN0YXJ0IFggY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRl9ZMCIgQWxpYXM9IlN0YXJ0IFkwIiAgICAgICAgICAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBzdGFydCBZIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZfWDEiIEFsaWFzPSJFbmQgWDEiICAgICAgICAgICAgICAgIFVuaXQ9Im0iICAgIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZnJhY3R1cmUgZW5kIFggY29vcmRpbmF0ZSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRl9ZMSIgQWxpYXM9IkVuZCBZMSIgICAgICAgICAgICAgICAgVW5pdD0ibSIgICAgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmcmFjdHVyZSBlbmQgWSBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGX0ZDIiBBbGlhcz0iRnJhY3R1cmUgQ29uZHVjdGl2aXR5IiBVbml0PSJtZC5tIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICAgRGlnaXQ9IjYiIERlc2M9ImZyYWN0dXJlIGNvbmR1Y3Rpdml0eSIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSJGYXVsdCBQYXJhbWV0ZXJzIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iRlRfWDAiIEFsaWFzPSJTdGFydCBYMCIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBzdGFydCBYIGNvb3JkaW5hdGUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IkZUX1kwIiBBbGlhcz0iU3RhcnQgWTAiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iZmF1bHQgc3RhcnQgWSBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9YMSIgQWxpYXM9IkVuZCBYMSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBlbmQgWCBjb29yZGluYXRlIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJGVF9ZMSIgQWxpYXM9IkVuZCBZMSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJmYXVsdCBlbmQgWSBjb29yZGluYXRlIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IlJlY3RhbmdsZSBCb3VuZGFyeSBQYXJhbWV0ZXJzIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1pbiIgQWxpYXM9IkxlZnQgQm91bmRhcnkgWCIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJyZWN0YW5nbGUgYm91bmRhcnkgbWluaW11bSBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUl9ZTWluIiBBbGlhcz0iQm90dG9tIEJvdW5kYXJ5IFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1pbmltdW0gWSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWE1heCIgQWxpYXM9IlJpZ2h0IEJvdW5kYXJ5IFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0icmVjdGFuZ2xlIGJvdW5kYXJ5IG1heGltdW0gWCIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlJfWU1heCIgQWxpYXM9IlRvcCBCb3VuZGFyeSBZIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9InJlY3RhbmdsZSBib3VuZGFyeSBtYXhpbXVtIFkiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0iQ2lyY2xlIEJvdW5kYXJ5IFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19DZW50ZXJYIiBBbGlhcz0iQ2VudGVyIFgiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IGNlbnRlciBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19DZW50ZXJZIiBBbGlhcz0iQ2VudGVyIFkiIFVuaXQ9Im0iIERlZmF1bHQ9IjAiIE1heD0iMTAwMDAiIE1pbj0iLTEwMDAwIiBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IGNlbnRlciBZIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCQ19SYWRpdXMiICBBbGlhcz0iUmFkaXVzIiAgIFVuaXQ9Im0iIERlZmF1bHQ9IjEiIE1heD0iMTAwMDAiIE1pbj0iMWUtMDUiICBEaWdpdD0iNiIgRGVzYz0iY2lyY2xlIGJvdW5kYXJ5IHJhZGl1cyIgLz4NCiAgPC9QYXJhR3JvdXA+DQoNCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSJQb2x5Z29uIEJvdW5kYXJ5IFZlcnRleCBQYXJhbWV0ZXJzIj4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iQlBfWCIgQWxpYXM9IlggQ29vcmRpbmF0ZSIgVW5pdD0ibSIgRGVmYXVsdD0iMCIgTWF4PSIxMDAwMCIgTWluPSItMTAwMDAiIERpZ2l0PSI2IiBEZXNjPSJwb2x5Z29uIGJvdW5kYXJ5IHZlcnRleCBYIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJCUF9ZIiBBbGlhcz0iWSBDb29yZGluYXRlIiBVbml0PSJtIiBEZWZhdWx0PSIwIiBNYXg9IjEwMDAwIiBNaW49Ii0xMDAwMCIgRGlnaXQ9IjYiIERlc2M9InBvbHlnb24gYm91bmRhcnkgdmVydGV4IFkiIC8+DQogIDwvUGFyYUdyb3VwPg0KDQogIDxQYXJhR3JvdXAgTmFtZT0iIiBBbGlhcz0iUmVnaW9uIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSX0xlYWthZ2UiIEFsaWFzPSJMZWFrYWdlIENvZWZmaWNpZW50IiBVbml0PSIiIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMCIgRGlnaXQ9IjYiIERlc2M9InJlZ2lvbiBsZWFrYWdlIGNvZWZmaWNpZW50IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8UGFyYUdyb3VwIE5hbWU9IiIgQWxpYXM9IlJlZ2lvbiBNYXJrIFBhcmFtZXRlcnMiPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSTV9Db21XIiAgICAgICBBbGlhcz0iU3RvcmFnZSBSYXRpbyIgVW5pdD0iIiBEZWZhdWx0PSIxIiBNYXg9IjEiICAgIE1pbj0iMCIgICAgIERpZ2l0PSI2IiBEZXNjPSJzdG9yYWdlIHJhdGlvIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSTV9Db21LciIgICAgICBBbGlhcz0iTW9iaWxpdHkgUmF0aW8iIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxMDAwIiBNaW49IjAuMDAxIiBEaWdpdD0iNiIgRGVzYz0ibW9iaWxpdHkgcmF0aW8iIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJNX05ldFRvR3Jvc3MiIEFsaWFzPSJOZXQtdG8tR3Jvc3MiIFVuaXQ9IiIgRGVmYXVsdD0iMSIgTWF4PSIxIiAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgRGVzYz0ibmV0LXRvLWdyb3NzIiAvPg0KICA8L1BhcmFHcm91cD4NCg0KICA8IS0tIFJlc3VsdCBwYXJhbWV0ZXJzIGFyZSBzZWxlY3RlZCBvbmx5IGJ5IHJlYnVpbGRSZXN1bHRQYXJhcygpLiAtLT4NCiAgPFBhcmFHcm91cCBOYW1lPSIiIEFsaWFzPSJSZXN1bHQgUGFyYW1ldGVycyI+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX1J3IiAgIEFsaWFzPSJXZWxsIFJhZGl1cyIgICAgICAgICAgICAgVW5pdD0ibSIgICAgICBEZWZhdWx0PSIwLjEiICAgIE1heD0iMTAiICAgIE1pbj0iMC4wMDEiICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3ZWxsYm9yZSByYWRpdXMiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX0RmYyIgIEFsaWFzPSJGcmFjdHVyZSBDb25kdWN0aXZpdHkiICBVbml0PSJtZC5tIiAgIERlZmF1bHQ9IjEwMDAiICAgTWF4PSIxZSszMCIgTWluPSIwIiAgICAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IGZyYWN0dXJlIGNvbmR1Y3Rpdml0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1dfU2tpbiIgQWxpYXM9IlNraW4gRmFjdG9yIiAgICAgICAgICAgIFVuaXQ9IiIgICAgICAgRGVmYXVsdD0iMCIgICAgICBNYXg9IjEwMDAiICBNaW49Ii0xMDAiICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgc2tpbiBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9XX0MiICAgIEFsaWFzPSJXZWxsYm9yZSBTdG9yYWdlIENvZWZmIiBVbml0PSJtXjMvTVBhIiBEZWZhdWx0PSIwIiAgICAgIE1heD0iMTAwMCIgIE1pbj0iMCIgICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCB3ZWxsYm9yZSBzdG9yYWdlIGNvZWZmaWNpZW50IiAvPg0KDQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9QaSIgIEFsaWFzPSJJbml0aWFsIEZvcm1hdGlvbiBQcmVzc3VyZSIgVW5pdD0iTVBhIiAgIERlZmF1bHQ9IjQwIiAgICAgTWF4PSI1MDAiICAgTWluPSIwLjEiICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgaW5pdGlhbCBmb3JtYXRpb24gcHJlc3N1cmUiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9LIiAgIEFsaWFzPSJQZXJtZWFiaWxpdHkiICAgICAgICAgICAgICAgVW5pdD0iRGFyY3kiIERlZmF1bHQ9IjAuMDAxIiAgTWF4PSIxMCIgICAgTWluPSIwIiAgICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgcGVybWVhYmlsaXR5IiAvPgogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9oIiAgIEFsaWFzPSJSZXNlcnZvaXIgVGhpY2tuZXNzIiAgICAgICAgVW5pdD0ibSIgICAgIERlZmF1bHQ9IjEwIiAgICAgTWF4PSIxMDAwMCIgTWluPSIxZS0wNSIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgcmVzZXJ2b2lyIHRoaWNrbmVzcyIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X3BoaSIgQWxpYXM9IlBvcm9zaXR5IiAgICAgICAgICAgICAgICAgICBVbml0PSIiICAgICAgRGVmYXVsdD0iMC4xIiAgICBNYXg9IjEiICAgICBNaW49IjFlLTA1IiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBwb3Jvc2l0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0N0aSIgQWxpYXM9IlRvdGFsIENvbXByZXNzaWJpbGl0eSIgICAgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMC4wMDEiICBNYXg9IjEwIiAgICBNaW49IjFlLTMwIiBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InJlc3VsdCB0b3RhbCBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9DZiIgIEFsaWFzPSJSb2NrIENvbXByZXNzaWJpbGl0eSIgICAgICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAuMDAwMSIgTWF4PSIxMCIgICAgTWluPSIxZS0zMCIgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgcm9jayBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9Tb2kiIEFsaWFzPSJJbml0aWFsIE9pbCBTYXR1cmF0aW9uIiAgICAgVW5pdD0iIiAgICAgIERlZmF1bHQ9IjAuOCIgICAgTWF4PSIxIiAgICAgTWluPSIwIiAgICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgaW5pdGlhbCBvaWwgc2F0dXJhdGlvbiIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X1NnaSIgQWxpYXM9IkluaXRpYWwgR2FzIFNhdHVyYXRpb24iICAgICBVbml0PSIiICAgICAgRGVmYXVsdD0iMCIgICAgICBNYXg9IjEiICAgICBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBpbml0aWFsIGdhcyBzYXR1cmF0aW9uIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfU3dpIiBBbGlhcz0iSW5pdGlhbCBXYXRlciBTYXR1cmF0aW9uIiAgIFVuaXQ9IiIgICAgICBEZWZhdWx0PSIwLjIiICAgIE1heD0iMSIgICAgIE1pbj0iMCIgICAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IGluaXRpYWwgd2F0ZXIgc2F0dXJhdGlvbiIgLz4NCg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQm8iICAgQWxpYXM9Ik9pbCBGb3JtYXRpb24gVm9sdW1lIEZhY3RvciIgICBVbml0PSJtMy9tMyIgRGVmYXVsdD0iMSIgTWF4PSIxZSszMCIgTWluPSIwIiAgICAgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgb2lsIGZvcm1hdGlvbiB2b2x1bWUgZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfTWl1byIgQWxpYXM9Ik9pbCBWaXNjb3NpdHkiICAgICAgICAgICAgICAgICBVbml0PSJtUGEucyIgRGVmYXVsdD0iMSIgTWF4PSIxZSszMCIgTWluPSIxZS0zMCIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgb2lsIHZpc2Nvc2l0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0NvIiAgIEFsaWFzPSJPaWwgQ29tcHJlc3NpYmlsaXR5IiAgICAgICAgICAgVW5pdD0iMS9NUGEiIERlZmF1bHQ9IjAiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgRGlnaXQ9IjEwIiBTY2llbnRpZmljPSIxIiBEZXNjPSJyZXN1bHQgb2lsIGNvbXByZXNzaWJpbGl0eSIgLz4NCiAgICA8UGFyYUl0ZW0gTmFtZT0iUmVzdWx0X0J3IiAgIEFsaWFzPSJXYXRlciBGb3JtYXRpb24gVm9sdW1lIEZhY3RvciIgVW5pdD0ibTMvbTMiIERlZmF1bHQ9IjEiIE1heD0iMWUrMzAiIE1pbj0iMCIgICAgIERpZ2l0PSI2IiAgRGVzYz0icmVzdWx0IHdhdGVyIGZvcm1hdGlvbiB2b2x1bWUgZmFjdG9yIiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfTWl1dyIgQWxpYXM9IldhdGVyIFZpc2Nvc2l0eSIgICAgICAgICAgICAgICBVbml0PSJtUGEucyIgRGVmYXVsdD0iMSIgTWF4PSIxZSszMCIgTWluPSIxZS0zMCIgRGlnaXQ9IjYiICBEZXNjPSJyZXN1bHQgd2F0ZXIgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ3ciICAgQWxpYXM9IldhdGVyIENvbXByZXNzaWJpbGl0eSIgICAgICAgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMCIgTWF4PSIxZSszMCIgTWluPSIwIiAgICBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InJlc3VsdCB3YXRlciBjb21wcmVzc2liaWxpdHkiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9CZyIgICBBbGlhcz0iR2FzIEZvcm1hdGlvbiBWb2x1bWUgRmFjdG9yIiAgIFVuaXQ9Im0zL20zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjAiICAgICBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgZm9ybWF0aW9uIHZvbHVtZSBmYWN0b3IiIC8+DQogICAgPFBhcmFJdGVtIE5hbWU9IlJlc3VsdF9NaXVnIiBBbGlhcz0iR2FzIFZpc2Nvc2l0eSIgICAgICAgICAgICAgICAgIFVuaXQ9Im1QYS5zIiBEZWZhdWx0PSIxIiBNYXg9IjFlKzMwIiBNaW49IjFlLTMwIiBEaWdpdD0iNiIgIERlc2M9InJlc3VsdCBnYXMgdmlzY29zaXR5IiAvPg0KICAgIDxQYXJhSXRlbSBOYW1lPSJSZXN1bHRfQ2ciICAgQWxpYXM9IkdhcyBDb21wcmVzc2liaWxpdHkiICAgICAgICAgICBVbml0PSIxL01QYSIgRGVmYXVsdD0iMCIgTWF4PSIxZSszMCIgTWluPSIwIiAgICBEaWdpdD0iMTAiIFNjaWVudGlmaWM9IjEiIERlc2M9InJlc3VsdCBnYXMgY29tcHJlc3NpYmlsaXR5IiAvPg0KICA8L1BhcmFHcm91cD4NCg0KPC9Sb290Pg0K
\ No newline at end of file
diff --git a/Bin/Config/Common/UnitDefault_cn.dat b/Bin/Config/Common/UnitDefault_cn.dat
index 7e3dd09..6271b6b 100644
--- a/Bin/Config/Common/UnitDefault_cn.dat
+++ b/Bin/Config/Common/UnitDefault_cn.dat
@@ -1 +1 @@
-77u/PD94bWwgcup_mpadmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxSb290Pg0KICAgIDxUeXwzyBlT2Y+DQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMDHnu4TvvJrplb/luqYgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibGVuZ3RoIiBOYW1lPSJsZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMTAxIiBVbml0PSJtIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDEwMiIgVW5pdD0ia20iIGs9IjEwMDAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDMiIFVuaXQ9ImNtIiBrPSIwLjAxIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMTA0IiBVbml0PSJtbSIgaz0iMC4wMDEiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDUiIFVuaXQ9ImluIiBrPSIwLjAyNTQiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDYiIFVuaXQ9ImZ0IiBrPSIwLjMwNDgiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDciIFVuaXQ9InlkIiBrPSIwLjkxNDQiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDgiIFVuaXQ9Im1pIiBrPSIxNjA5LjM0IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMTA5IiBVbml0PSJubWlsZSIgaz0iMTg1MiIgRGlnaXQ9IjAiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDExMCIgVW5pdD0ibm0iIGs9IjFlLTA5IiBEaWdpdD0iNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDAy57uE77ya5pe26Ze0ID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRpbWUiIE5hbWU9InRpbWUiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMjAxIiBVbml0PSJzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDIwMiIgVW5pdD0ibXMiIGs9IjAuMDAxIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMjAzIiBVbml0PSJ1cyIgaz0iMWUtMDYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAyMDQiIFVuaXQ9Im1pbiIgaz0iNjAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAyMDUiIFVuaXQ9ImgiIGs9IjM2MDAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAyMDYiIFVuaXQ9ImQiIGs9Ijg2NDAwIiBEaWdpdD0iMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDAz57uE77ya6LSo6YePID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9Im1hc3MiIE5hbWU9Im1hc3MiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzAxIiBVbml0PSJrZyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAzMDIiIFVuaXQ9ImciIGs9IjAuMDAxIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzAzIiBVbml0PSJ0b24iIGs9IjEwMDAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAzMDQiIFVuaXQ9ImxiIiBrPSIwLjQ1MzU5MjM3IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzA1IiBVbml0PSJveiIgaz0iMC4wMjgzNDk1IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzA2IiBVbml0PSJzdG9uIiBrPSI5MDcuMTg1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzA3IiBVbml0PSJsdG9uIiBrPSIxMDE2LjA1IiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA057uE77ya6KeS5bqmID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9ImFuZ2xlIiBOYW1lPSJhbmdsZSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA0MDEiIFVuaXQ9InJhZCIgaz0iMSIgRGlnaXQ9IjYiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA0MDIiIFVuaXQ9ImRlZyIgaz0iMC4wMTc0NTMyOTI1MTk5NDMzIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNDAzIiBVbml0PSJncmFkIiBrPSIwLjAxNTcwNzk2MzI2Nzk0OSIgRGlnaXQ9IjYiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwwNee7hO+8mumdouenryA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJhcmVhIiBOYW1lPSJhcmVhIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDUwMSIgVW5pdD0ibcKyIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDUwMiIgVW5pdD0ia23CsiIgaz0iMWU2IiBEaWdpdD0iMCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNTAzIiBVbml0PSJjbcKyIiBrPSIwLjAwMDEiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA1MDQiIFVuaXQ9ImZ0wrIiIGs9IjAuMDkyOTAzMDQiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA1MDUiIFVuaXQ9ImluwrIiIGs9IjAuMDAwNjQ1MTYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA1MDYiIFVuaXQ9ImFjcmUiIGs9IjQwNDYuODU2NDIyNCIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDUwNyIgVW5pdD0iaGEiIGs9IjEwMDAwIiBEaWdpdD0iMCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNTA4IiBVbml0PSJ5ZMKyIiBrPSIwLjgzNjEyNzM2IiBEaWdpdD0iNCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA257uE77ya5L2T56evID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZvbHVtZSIgTmFtZT0idm9sdW1lIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwMSIgVW5pdD0ibcKzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwMiIgVW5pdD0iTCIgaz0iMC4wMDEiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDMiIFVuaXQ9Im1MIiBrPSIxZS0wNiIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwNCIgVW5pdD0iY23CsyIgaz0iMWUtMDYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDUiIFVuaXQ9ImNjIiBrPSIxZS0wNiIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwNiIgVW5pdD0iYmJsIiBrPSIwLjE1ODk4NzI5NDkyOCIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwNyIgVW5pdD0iZ2FsKFVTKSIgaz0iMC4wMDM3ODU0MTE3ODQiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDgiIFVuaXQ9ImdhbChVSykiIGs9IjAuMDA0NTQ2MDkiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDkiIFVuaXQ9ImZ0wrMiIGs9IjAuMDI4MzE2ODQ2NTkyIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNjEwIiBVbml0PSJpbsKzIiBrPSIxLjYzODcwNjRlLTA1IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNjExIiBVbml0PSJxdCIgaz0iMC4wMDA5NDYzNTI5NDYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MTIiIFVuaXQ9InB0IiBrPSIwLjAwMDQ3MzE3NjQ3MyIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYxMyIgVW5pdD0iZ2kiIGs9IjAuMDAwMTE4Mjk0MTE4IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNjE0IiBVbml0PSJhY3JlLmZ0IiBrPSIxMjMzLjQ4MTgzNzU0NzUyIiBEaWdpdD0iMyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA357uE77ya6YCf5bqmID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZlbG9jaXR5IiBOYW1lPSJ2ZWxvY2l0eSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA3MDEiIFVuaXQ9Im0vcyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA3MDIiIFVuaXQ9ImttL2giIGs9IjAuMjc3Nzc3Nzc4IiBEaWdpdD0iNSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNzAzIiBVbml0PSJmdC9zIiBrPSIwLjMwNDgiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA3MDQiIFVuaXQ9Im1pbGUvaCIgaz0iMC40NDcwNCIgRGlnaXQ9IjUiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwwOOe7hO+8mua4qeW6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ0ZW1wZXJhdHVyZSIgTmFtZT0idGVtcGVyYXR1cmUiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwODAxIiBVbml0PSJLIiBrPSIxIiBiPSIwIiBEaWdpdD0iMiIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDgwMiIgVW5pdD0i4oSDIiBrPSIxIiBiPSIyNzMuMTUiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA4MDMiIFVuaXQ9ImRlZ0MiIGs9IjEiIGI9IjI3My4xNSIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDgwNCIgVW5pdD0iZGVnRiIgaz0iMC41NTU1NTU1NTU1NTU1NTYiIGI9IjI1NS4zNzIyMjIyMjIyMjIiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA4MDUiIFVuaXQ9IsKwRiIgaz0iMC41NTU1NTU1NTU1NTU1NTYiIGI9IjI1NS4zNzIyMjIyMjIyMjIiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA4MDYiIFVuaXQ9IlIiIGs9IjAuNTU1NTU1NTU1NTU1NTU2IiBiPSIwIiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA557uE77ya5rip5beuID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRlbXBlcmF0dXJlX2RlbHRhIiBOYW1lPSJ0ZW1wZXJhdHVyZV9kZWx0YSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA5MDEiIFVuaXQ9IksiIGs9IjEiIERpZ2l0PSIyIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwOTAyIiBVbml0PSLihIMiIGs9IjEiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA5MDMiIFVuaXQ9ImRlZ0MiIGs9IjEiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA5MDQiIFVuaXQ9ImRlZ0YiIGs9IjAuNTU1NTU1NTU1NTU1NTU2IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwOTA1IiBVbml0PSLCsEYiIGs9IjAuNTU1NTU1NTU1NTU1NTU2IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwOTA2IiBVbml0PSJSIiBrPSIwLjU1NTU1NTU1NTU1NTU1NiIgRGlnaXQ9IjIiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxMOe7hO+8muWcsOa4qeair+W6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJnZW90aGVybWFsZ3JhZGllbnQiIE5hbWU9Imdlb3RoZXJtYWxncmFkaWVudCI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDEiIFVuaXQ9IksvbSIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDIiIFVuaXQ9IuKEgy9rbSIgaz0iMC4wMDEiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDMiIFVuaXQ9ImRlZy9rbSIgaz0iMC4wMDEiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDQiIFVuaXQ9IsKwRi9mdCIgaz0iMS44MTgxODE4MTgxODE4MiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTAwNSIgVW5pdD0iRi9mdCIgaz0iMS44MTgxODE4MTgxODE4MiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTAwNiIgVW5pdD0iwrBGLzEwMGZ0IiBrPSIwLjAxODE4MTgxODE4MTgxODIiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDciIFVuaXQ9IkYvbWlsZSIgaz0iMC4wMzQ0ODI3NTg2MjA2ODk3IiBEaWdpdD0iMyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDEx57uE77ya5Y6L5YqbID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InByZXNzdXJlIiBOYW1lPSJwcmVzc3VyZSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTExMDEiIFVuaXQ9IlBhIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwMiIgVW5pdD0ia1BhIiBrPSIxMDAwIiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTAzIiBVbml0PSJNUGEiIGs9IjFlNiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwNCIgVW5pdD0iYmFyIiBrPSIxMDAwMDAiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTExMDUiIFVuaXQ9Im1iYXIiIGs9IjEwMCIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwNiIgVW5pdD0icHNpIiBrPSI2ODk0Ljc1NzI5IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTA3IiBVbml0PSJhdG0iIGs9IjEwMTMyNSIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwOCIgVW5pdD0iYXQiIGs9Ijk4MDY2LjUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTExMDkiIFVuaXQ9ImtnZi9jbcKyIiBrPSI5ODA2Ni41IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTEwIiBVbml0PSJtbUhnIiBrPSIxMzMuMzIyMzY4IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTExIiBVbml0PSJUb3JyIiBrPSIxMzMuMzIyMzY4IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTEyIiBVbml0PSJtSDJPIiBrPSI5ODA2LjY1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTEzIiBVbml0PSJtbUgyTyIgaz0iOS44MDY2NSIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTExNCIgVW5pdD0iZHluL2NtwrIiIGs9IjAuMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxMue7hO+8muWvhuW6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJkZW5zaXR5IiBOYW1lPSJkZW5zaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTIwMSIgVW5pdD0ia2cvbcKzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTIwMiIgVW5pdD0iZy9jbcKzIiBrPSIxMDAwIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjAzIiBVbml0PSJnL2NjIiBrPSIxMDAwIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA0IiBVbml0PSJsYi9mdMKzIiBrPSIxNi4wMTg0NjM0IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA1IiBVbml0PSJsYi9nYWwoVVMpIiBrPSIxMTkuODI2NDI3IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA2IiBVbml0PSJsYi9nYWwoVUspIiBrPSI5OS43NzY0IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA3IiBVbml0PSJsYi9iYmwiIGs9IjIuODUzIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA4IiBVbml0PSJnL2Z0wrMiIGs9IjAuMDM1MzE0NyIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTIwOSIgVW5pdD0iZy9jZiIgaz0iMC4wMzUzMTQ3IiBEaWdpdD0iMyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDEz57uE77ya5Yqo5Yqb57KY5bqmID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZpc2Nvc2l0eSIgTmFtZT0idmlzY29zaXR5X2R5bmFtaWMiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMzAxIiBVbml0PSJQYcK3cyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEzMDIiIFVuaXQ9Im1QYcK3cyIgaz0iMC4wMDEiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEzMDMiIFVuaXQ9ImNQIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTMwNCIgVW5pdD0iUCIgaz0iMC4xIiBEaWdpdD0iNCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMzA1IiBVbml0PSJrZ2bCt3MvbcKyIiBrPSI5LjgwNjY1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMzA2IiBVbml0PSJsYmbCt3MvZnTCsiIgaz0iNDcuODgwMyIgRGlnaXQ9IjMiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxNOe7hO+8mui/kOWKqOeymOW6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHlfa2luZW1hdGljIiBOYW1lPSJ2aXNjb3NpdHlfa2luZW1hdGljIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTQwMSIgVW5pdD0ibcKyL3MiIGs9IjEiIERpZ2l0PSI2IiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDAyIiBVbml0PSJtbcKyL3MiIGs9IjFlLTA2IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDAzIiBVbml0PSJjU3QiIGs9IjFlLTA2IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDA0IiBVbml0PSJTdCIgaz0iMC4wMDAxIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDA1IiBVbml0PSJjbcKyL3MiIGs9IjAuMDAwMSIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTQwNiIgVW5pdD0iZnTCsi9zIiBrPSIwLjA5MjkwMzA0IiBEaWdpdD0iNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDE157uE77ya5riX6YCP546HID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcm1lYWJpbGl0eSIgTmFtZT0icGVybWVhYmlsaXR5Ij4NCg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNTAxIiBVbml0PSJEIiBrPSIxIiBEaWdpdD0iNCIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTUwMiIgVW5pdD0ibUQiIGs9IjAuMDAxIiBEaWdpdD0iNCIgLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTUwMyIgVW5pdD0iMS9jbcKyIiBrPSI5LjgxZSswNyIgRGlnaXQ9IjIiLz4NCg0KDQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMTbnu4TvvJrog73ph48gPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZW5lcmd5IiBOYW1lPSJlbmVyZ3kiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNjAxIiBVbml0PSJKIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTYwMiIgVW5pdD0ia0oiIGs9IjEwMDAiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDMiIFVuaXQ9Ik1KIiBrPSIxZTYiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDQiIFVuaXQ9ImNhbCIgaz0iNC4xODQiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDUiIFVuaXQ9ImtjYWwiIGs9IjQxODQiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDYiIFVuaXQ9IkJ0dSIgaz0iMTA1NS4wNTU4NTI2MiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTYwNyIgVW5pdD0iQlRVIiBrPSIxMDU1LjA1NTg1MjYyIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNjA4IiBVbml0PSJrV8K3aCIgaz0iMy42ZTYiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDkiIFVuaXQ9ImZ0wrdsYmYiIGs9IjEuMzU1ODE3OTQ4IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNjEwIiBVbml0PSJrZ2bCt20iIGs9IjkuODA2NjUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MTEiIFVuaXQ9ImhwwrdoIiBrPSIyLjY4NDUyZTYiIERpZ2l0PSIzIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMTfnu4TvvJrlip/njocgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icG93ZXIiIE5hbWU9InBvd2VyIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTcwMSIgVW5pdD0iVyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE3MDIiIFVuaXQ9ImtXIiBrPSIxMDAwIiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNzAzIiBVbml0PSJNVyIgaz0iMWU2IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNzA0IiBVbml0PSJocCIgaz0iNzQ1LjY5OTg3MTU4MjI3IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNzA1IiBVbml0PSJCdHUvaCIgaz0iMC4yOTMwNzEwNyIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTcwNiIgVW5pdD0iY2FsL3MiIGs9IjQuMTg2OCIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTcwNyIgVW5pdD0ia2dmwrdtL3MiIGs9IjkuODA2NjUiIERpZ2l0PSIzIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMTjnu4TvvJrkvZPnp6/mtYHph48gPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZmxvd19yYXRlX3ZvbHVtZSIgTmFtZT0iZmxvd19yYXRlX3ZvbHVtZSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE4MDEiIFVuaXQ9Im3Csy9kIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwMiIgVW5pdD0ibcKzL3MiIGs9Ijg2NDAwIiBEaWdpdD0iNiIgUmVtYXJrPSIxIG3Csy9zID0gODY0MDAgbcKzL2QiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwMyIgVW5pdD0ibcKzL2EiIGs9IjAuMDAyNzM5NzI2MDI3Mzk3MjYiIERpZ2l0PSI2IiBSZW1hcms9IjEgbcKzL2EgPSAxLzM2NSBtwrMvZCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxODA0IiBVbml0PSJzdGIvZCIgaz0iMSIgRGlnaXQ9IjYiIFJlbWFyaz0iMSBzdGIvZCDiiYggMSBtwrMvZCAo5bel56iL6L+R5Ly8KSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxODA1IiBVbml0PSJiYmwvZCIgaz0iMSIgRGlnaXQ9IjYiIFJlbWFyaz0iMSBiYmwvZCDiiYggMSBtwrMvZCAo5bel56iL6L+R5Ly8KSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxODA2IiBVbml0PSJmdMKzL3MiIGs9IjI0NDYuNTc1IiBEaWdpdD0iNiIgUmVtYXJrPSIxIGZ0wrMvcyA9IDI0NDYuNTc1IG3Csy9kIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE4MDciIFVuaXQ9ImZ0wrMvZCIgaz0iMC4wMjgzMTY4NDY1OTIiIERpZ2l0PSI2IiBSZW1hcms9IjEgZnTCsy9kID0gMC4wMjgzMTY4NDY1OTIgbcKzL2QiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwOCIgVW5pdD0iTU1zY2YvZCIgaz0iMjgzMTYuODQ2NTkyIiBEaWdpdD0iNCIgUmVtYXJrPSIxIE1Nc2NmL2QgPSAyODMxNi44NDY1OTIgbcKzL2QgKOeQhuaDs+awlOS9kykiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwOSIgVW5pdD0iTXNjZi9kIiBrPSIyOC4zMTY4NDY1OTIiIERpZ2l0PSI2IiBSZW1hcms9IjEgTXNjZi9kID0gMjguMzE2ODQ2NTkyIG3Csy9kICjnkIbmg7PmsJTkvZMpIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE4MTAiIFVuaXQ9IjEw4oG0Km3Csy9kIiBrPSIxMDAwMCIgRGlnaXQ9IjQiIFJlbWFyaz0iMSDkuIfmlrkv5aSpID0gMTAwMDAgbcKzL2QiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxOee7hO+8muWOi+e8qeezu+aVsCA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJjb21wcmVzc2liaWxpdHkiIE5hbWU9ImNvbXByZXNzaWJpbGl0eSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE5MDEiIFVuaXQ9IjEvTVBhIiBrPSIxIiBEaWdpdD0iNiIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTkwMiIgVW5pdD0iMS9QYSIgaz0iMWU2IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxOTAzIiBVbml0PSIxL3BzaSIgaz0iMTQ1LjAzNzczOCIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTkwNCIgVW5pdD0iMS9hdG0iIGs9IjkuODY5MjMyNjY3MTYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE5MDUiIFVuaXQ9IjEvYmFyIiBrPSIxMCIgRGlnaXQ9IjYiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwyMOe7hO+8mua1k+W6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJjb25jZW50cmF0aW9uIiBOYW1lPSJjb25jZW50cmF0aW9uIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwMSIgVW5pdD0ia2cvbcKzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwMiIgVW5pdD0iZy9MIiBrPSIxIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMDAzIiBVbml0PSJtZy9MIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwNCIgVW5pdD0izrxnL21MIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwNSIgVW5pdD0icHBtIiBrPSIwLjAwMSIgRGlnaXQ9IjQiIFJlbWFyaz0iYXNzdW1pbmcgMSBwcG0gPSAxIG1nL2tnIGluIHdhdGVyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIwMDYiIFVuaXQ9IktwcG0iIGs9IjEiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIwMDciIFVuaXQ9Im1vbC9twrMiIGs9IjEiIERpZ2l0PSIzIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMjHnu4TvvJrnmb7liIbmr5QgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icGVyY2VudCIgTmFtZT0icGVyY2VudCI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIxMDEiIFVuaXQ9IiUiIGs9IjEiIERpZ2l0PSIyIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMTAyIiBVbml0PSJwdSIgaz0iMSIgRGlnaXQ9IjIiIFJlbWFyaz0icGVyIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjEwMyIgVW5pdD0iZnJhY3Rpb24iIGs9IjEwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjEwNCIgVW5pdD0idi92IiBrPSIxMDAiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIxMDUiIFVuaXQ9Im3Csy9twrMiIGs9IjEwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwyMue7hO+8muaFouW6pu+8iOWjsOazou+8iSA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzbG93bmVzcyIgTmFtZT0ic2xvd25lc3MiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMjAxIiBVbml0PSJzL20iIGs9IjEiIERpZ2l0PSIzIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMjAyIiBVbml0PSJ1cy9tIiBrPSIxZS0wNiIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjIwMyIgVW5pdD0idXMvZnQiIGs9IjMuMjgwODM5ODk1MDEzMTJlLTA2IiBEaWdpdD0iNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDIz57uE77ya5pS+5bCE5oCnID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InJhZGlvYWN0aXZpdHkiIE5hbWU9InJhZGlvYWN0aXZpdHkiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMzAxIiBVbml0PSJBUEkiIGs9IjEiIERpZ2l0PSIxIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMzAyIiBVbml0PSJnQVBJIiBrPSIxIiBEaWdpdD0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDI057uE77ya55S15Y6LID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZvbHRhZ2UiIE5hbWU9InZvbHRhZ2UiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNDAxIiBVbml0PSJWIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjQwMiIgVW5pdD0ibVYiIGs9IjAuMDAxIiBEaWdpdD0iNCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNDAzIiBVbml0PSJrViIgaz0iMTAwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwyNee7hO+8muWKmyA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJmb3JjZSIgTmFtZT0iZm9yY2UiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNTAxIiBVbml0PSJOIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjUwMiIgVW5pdD0ia04iIGs9IjEwMDAiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI1MDMiIFVuaXQ9ImxiZiIgaz0iNC40NDgyMjE2MTUyNjA1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNTA0IiBVbml0PSJrZ2YiIGs9IjkuODA2NjUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI1MDUiIFVuaXQ9ImR5biIgaz0iMWUtMDUiIERpZ2l0PSI2Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMjbnu4TvvJrpopHnjocgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZnJlcXVlbmN5IiBOYW1lPSJmcmVxdWVuY3kiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNjAxIiBVbml0PSJIeiIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI2MDIiIFVuaXQ9ImtIeiIgaz0iMTAwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjYwMyIgVW5pdD0iTUh6IiBrPSIxZTYiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI2MDQiIFVuaXQ9IkdIeiIgaz0iMWU5IiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDI357uE77ya54Ot5a+8546HID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRoZXJtYWxfY29uZHVjdGl2aXR5IiBOYW1lPSJ0aGVybWFsX2NvbmR1Y3Rpdml0eSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI3MDEiIFVuaXQ9IlcvKG3Ct0spIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjcwMiIgVW5pdD0iVy8obcK34oSDKSIgaz0iMSIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjcwMyIgVW5pdD0iQnR1LyhowrdmdMK3wrBGKSIgaz0iMS43MzA3MzUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI3MDQiIFVuaXQ9ImNhbC8oY23Ct3PCt+KEgykiIGs9IjQxOC40IiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDI457uE77ya5q+U54Ot5a65ID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InNwZWNpZmljX2hlYXQiIE5hbWU9InNwZWNpZmljX2hlYXQiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyODAxIiBVbml0PSJKLyhrZ8K3SykiIGs9IjEiIERpZ2l0PSIzIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyODAyIiBVbml0PSJKLyhnwrdLKSIgaz0iMTAwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjgwMyIgVW5pdD0iY2FsLyhnwrfihIMpIiBrPSI0MTg0IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyODA0IiBVbml0PSJCdHUvKGxiwrfCsEYpIiBrPSI0MTg2LjgiIERpZ2l0PSIyIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMjnnu4TvvJrmkanlsJTotKjph48gPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibW9sYXJfbWFzcyIgTmFtZT0ibW9sYXJfbWFzcyI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI5MDEiIFVuaXQ9ImtnL21vbCIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI5MDIiIFVuaXQ9ImcvbW9sIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjkwMyIgVW5pdD0ibGIvbGJtb2wiIGs9IjAuNDUzNTkyMzciIERpZ2l0PSI0Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMzDnu4TvvJpVbmtub3duMSA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJtb2xhcl9tYXNzIiBOYW1lPSJtb2xhcl9tYXNzIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzAwMSIgVW5pdD0ia2cvbW9sIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzAwMiIgVW5pdD0iZy9tb2wiIGs9IjAuMDAxIiBEaWdpdD0iNCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUzMDAzIiBVbml0PSJsYi9sYm1vbCIgaz0iMC40NTM1OTIzNyIgRGlnaXQ9IjQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMzHnu4TvvJpVbmtub3duMSA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ3cyIgTmFtZT0i55uQ5bqmIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzEwMSIgVW5pdD0ibWcvTCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUzMTAyIiBVbml0PSLOvGcvbUwiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzEwMyIgVW5pdD0iZy9MIiBrPSIxMDAwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTMxMDQiIFVuaXQ9ImtnL21eMyIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzEwNSIgVW5pdD0icHBtIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTMxMDYiIFVuaXQ9IktwcG0iIGs9IjEwMDAiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwzMue7hO+8mlVua25vd24xID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZpc2Nvc2l0eSIgTmFtZT0idmlzY29zaXR5Ij4KICAgICAgICAgICAgPFVuaXQgSUQ9IlUzMjAxIiBVbml0PSJQYS5zIiBrPSIxIi8+CiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwMiIgVW5pdD0ibVBhLnMiIGs9IjAuMDAxIi8+CiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwMyIgVW5pdD0iUCIgaz0iMC4xIi8+CiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwNCIgVW5pdD0iY1AiIGs9IjAuMDAxIi8+CiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwNSIgVW5pdD0iYmYucy9mdF4yIiBrPSI0Ny44ODAzIi8+CiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwNiIgVW5pdD0ia2dmLnMvbV4zIiBrPSI5LjgwNjY1Ii8+CiAgICAgICAgPC9Vbml0R3JvdXA+CgogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMzPnu4TvvJrkupXnrZLlgqjpm4bns7vmlbAgPT09PT09PT09PT09PT09PT09PT0gLS0+CiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvMyIgTmFtZT0iVG9EbzMiPgogICAgICAgICAgICA8VW5pdCBJRD0iVTMzMDEiIFVuaXQ9Im1eMy9NUGEiIGs9IjEiIERpZ2l0PSI2IiBSZW1hcms9ImJhc2UgdW5pdCIvPgogICAgICAgIDwvVW5pdEdyb3VwPgoKICAgIDwvVHlwZU9mPgo8L1Jvb3Q+Cg==
\ No newline at end of file
+77u/PD94bWwgcup_mpadmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxSb290Pg0KICAgIDxUeXwzyBlT2Y+DQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMDHnu4TvvJrplb/luqYgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibGVuZ3RoIiBOYW1lPSJsZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMTAxIiBVbml0PSJtIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDEwMiIgVW5pdD0ia20iIGs9IjEwMDAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDMiIFVuaXQ9ImNtIiBrPSIwLjAxIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMTA0IiBVbml0PSJtbSIgaz0iMC4wMDEiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDUiIFVuaXQ9ImluIiBrPSIwLjAyNTQiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDYiIFVuaXQ9ImZ0IiBrPSIwLjMwNDgiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDciIFVuaXQ9InlkIiBrPSIwLjkxNDQiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAxMDgiIFVuaXQ9Im1pIiBrPSIxNjA5LjM0IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMTA5IiBVbml0PSJubWlsZSIgaz0iMTg1MiIgRGlnaXQ9IjAiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDExMCIgVW5pdD0ibm0iIGs9IjFlLTA5IiBEaWdpdD0iNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDAy57uE77ya5pe26Ze0ID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRpbWUiIE5hbWU9InRpbWUiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMjAxIiBVbml0PSJzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDIwMiIgVW5pdD0ibXMiIGs9IjAuMDAxIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMjAzIiBVbml0PSJ1cyIgaz0iMWUtMDYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAyMDQiIFVuaXQ9Im1pbiIgaz0iNjAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAyMDUiIFVuaXQ9ImgiIGs9IjM2MDAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAyMDYiIFVuaXQ9ImQiIGs9Ijg2NDAwIiBEaWdpdD0iMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDAz57uE77ya6LSo6YePID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9Im1hc3MiIE5hbWU9Im1hc3MiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzAxIiBVbml0PSJrZyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAzMDIiIFVuaXQ9ImciIGs9IjAuMDAxIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzAzIiBVbml0PSJ0b24iIGs9IjEwMDAiIERpZ2l0PSIwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTAzMDQiIFVuaXQ9ImxiIiBrPSIwLjQ1MzU5MjM3IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzA1IiBVbml0PSJveiIgaz0iMC4wMjgzNDk1IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzA2IiBVbml0PSJzdG9uIiBrPSI5MDcuMTg1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwMzA3IiBVbml0PSJsdG9uIiBrPSIxMDE2LjA1IiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA057uE77ya6KeS5bqmID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9ImFuZ2xlIiBOYW1lPSJhbmdsZSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA0MDEiIFVuaXQ9InJhZCIgaz0iMSIgRGlnaXQ9IjYiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA0MDIiIFVuaXQ9ImRlZyIgaz0iMC4wMTc0NTMyOTI1MTk5NDMzIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNDAzIiBVbml0PSJncmFkIiBrPSIwLjAxNTcwNzk2MzI2Nzk0OSIgRGlnaXQ9IjYiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwwNee7hO+8mumdouenryA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJhcmVhIiBOYW1lPSJhcmVhIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDUwMSIgVW5pdD0ibcKyIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDUwMiIgVW5pdD0ia23CsiIgaz0iMWU2IiBEaWdpdD0iMCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNTAzIiBVbml0PSJjbcKyIiBrPSIwLjAwMDEiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA1MDQiIFVuaXQ9ImZ0wrIiIGs9IjAuMDkyOTAzMDQiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA1MDUiIFVuaXQ9ImluwrIiIGs9IjAuMDAwNjQ1MTYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA1MDYiIFVuaXQ9ImFjcmUiIGs9IjQwNDYuODU2NDIyNCIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDUwNyIgVW5pdD0iaGEiIGs9IjEwMDAwIiBEaWdpdD0iMCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNTA4IiBVbml0PSJ5ZMKyIiBrPSIwLjgzNjEyNzM2IiBEaWdpdD0iNCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA257uE77ya5L2T56evID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZvbHVtZSIgTmFtZT0idm9sdW1lIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwMSIgVW5pdD0ibcKzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwMiIgVW5pdD0iTCIgaz0iMC4wMDEiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDMiIFVuaXQ9Im1MIiBrPSIxZS0wNiIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwNCIgVW5pdD0iY23CsyIgaz0iMWUtMDYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDUiIFVuaXQ9ImNjIiBrPSIxZS0wNiIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwNiIgVW5pdD0iYmJsIiBrPSIwLjE1ODk4NzI5NDkyOCIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYwNyIgVW5pdD0iZ2FsKFVTKSIgaz0iMC4wMDM3ODU0MTE3ODQiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDgiIFVuaXQ9ImdhbChVSykiIGs9IjAuMDA0NTQ2MDkiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MDkiIFVuaXQ9ImZ0wrMiIGs9IjAuMDI4MzE2ODQ2NTkyIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNjEwIiBVbml0PSJpbsKzIiBrPSIxLjYzODcwNjRlLTA1IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNjExIiBVbml0PSJxdCIgaz0iMC4wMDA5NDYzNTI5NDYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA2MTIiIFVuaXQ9InB0IiBrPSIwLjAwMDQ3MzE3NjQ3MyIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDYxMyIgVW5pdD0iZ2kiIGs9IjAuMDAwMTE4Mjk0MTE4IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNjE0IiBVbml0PSJhY3JlLmZ0IiBrPSIxMjMzLjQ4MTgzNzU0NzUyIiBEaWdpdD0iMyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA357uE77ya6YCf5bqmID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZlbG9jaXR5IiBOYW1lPSJ2ZWxvY2l0eSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA3MDEiIFVuaXQ9Im0vcyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA3MDIiIFVuaXQ9ImttL2giIGs9IjAuMjc3Nzc3Nzc4IiBEaWdpdD0iNSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwNzAzIiBVbml0PSJmdC9zIiBrPSIwLjMwNDgiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA3MDQiIFVuaXQ9Im1pbGUvaCIgaz0iMC40NDcwNCIgRGlnaXQ9IjUiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwwOOe7hO+8mua4qeW6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ0ZW1wZXJhdHVyZSIgTmFtZT0idGVtcGVyYXR1cmUiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwODAxIiBVbml0PSJLIiBrPSIxIiBiPSIwIiBEaWdpdD0iMiIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDgwMiIgVW5pdD0i4oSDIiBrPSIxIiBiPSIyNzMuMTUiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA4MDMiIFVuaXQ9ImRlZ0MiIGs9IjEiIGI9IjI3My4xNSIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMDgwNCIgVW5pdD0iZGVnRiIgaz0iMC41NTU1NTU1NTU1NTU1NTYiIGI9IjI1NS4zNzIyMjIyMjIyMjIiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA4MDUiIFVuaXQ9IsKwRiIgaz0iMC41NTU1NTU1NTU1NTU1NTYiIGI9IjI1NS4zNzIyMjIyMjIyMjIiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA4MDYiIFVuaXQ9IlIiIGs9IjAuNTU1NTU1NTU1NTU1NTU2IiBiPSIwIiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDA557uE77ya5rip5beuID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRlbXBlcmF0dXJlX2RlbHRhIiBOYW1lPSJ0ZW1wZXJhdHVyZV9kZWx0YSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA5MDEiIFVuaXQ9IksiIGs9IjEiIERpZ2l0PSIyIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwOTAyIiBVbml0PSLihIMiIGs9IjEiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA5MDMiIFVuaXQ9ImRlZ0MiIGs9IjEiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTA5MDQiIFVuaXQ9ImRlZ0YiIGs9IjAuNTU1NTU1NTU1NTU1NTU2IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwOTA1IiBVbml0PSLCsEYiIGs9IjAuNTU1NTU1NTU1NTU1NTU2IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUwOTA2IiBVbml0PSJSIiBrPSIwLjU1NTU1NTU1NTU1NTU1NiIgRGlnaXQ9IjIiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxMOe7hO+8muWcsOa4qeair+W6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJnZW90aGVybWFsZ3JhZGllbnQiIE5hbWU9Imdlb3RoZXJtYWxncmFkaWVudCI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDEiIFVuaXQ9IksvbSIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDIiIFVuaXQ9IuKEgy9rbSIgaz0iMC4wMDEiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDMiIFVuaXQ9ImRlZy9rbSIgaz0iMC4wMDEiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDQiIFVuaXQ9IsKwRi9mdCIgaz0iMS44MTgxODE4MTgxODE4MiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTAwNSIgVW5pdD0iRi9mdCIgaz0iMS44MTgxODE4MTgxODE4MiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTAwNiIgVW5pdD0iwrBGLzEwMGZ0IiBrPSIwLjAxODE4MTgxODE4MTgxODIiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEwMDciIFVuaXQ9IkYvbWlsZSIgaz0iMC4wMzQ0ODI3NTg2MjA2ODk3IiBEaWdpdD0iMyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDEx57uE77ya5Y6L5YqbID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InByZXNzdXJlIiBOYW1lPSJwcmVzc3VyZSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTExMDEiIFVuaXQ9IlBhIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwMiIgVW5pdD0ia1BhIiBrPSIxMDAwIiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTAzIiBVbml0PSJNUGEiIGs9IjFlNiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwNCIgVW5pdD0iYmFyIiBrPSIxMDAwMDAiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTExMDUiIFVuaXQ9Im1iYXIiIGs9IjEwMCIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwNiIgVW5pdD0icHNpIiBrPSI2ODk0Ljc1NzI5IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTA3IiBVbml0PSJhdG0iIGs9IjEwMTMyNSIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTEwOCIgVW5pdD0iYXQiIGs9Ijk4MDY2LjUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTExMDkiIFVuaXQ9ImtnZi9jbcKyIiBrPSI5ODA2Ni41IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTEwIiBVbml0PSJtbUhnIiBrPSIxMzMuMzIyMzY4IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTExIiBVbml0PSJUb3JyIiBrPSIxMzMuMzIyMzY4IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTEyIiBVbml0PSJtSDJPIiBrPSI5ODA2LjY1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMTEzIiBVbml0PSJtbUgyTyIgaz0iOS44MDY2NSIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTExNCIgVW5pdD0iZHluL2NtwrIiIGs9IjAuMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxMue7hO+8muWvhuW6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJkZW5zaXR5IiBOYW1lPSJkZW5zaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTIwMSIgVW5pdD0ia2cvbcKzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTIwMiIgVW5pdD0iZy9jbcKzIiBrPSIxMDAwIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjAzIiBVbml0PSJnL2NjIiBrPSIxMDAwIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA0IiBVbml0PSJsYi9mdMKzIiBrPSIxNi4wMTg0NjM0IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA1IiBVbml0PSJsYi9nYWwoVVMpIiBrPSIxMTkuODI2NDI3IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA2IiBVbml0PSJsYi9nYWwoVUspIiBrPSI5OS43NzY0IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA3IiBVbml0PSJsYi9iYmwiIGs9IjIuODUzIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMjA4IiBVbml0PSJnL2Z0wrMiIGs9IjAuMDM1MzE0NyIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTIwOSIgVW5pdD0iZy9jZiIgaz0iMC4wMzUzMTQ3IiBEaWdpdD0iMyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDEz57uE77ya5Yqo5Yqb57KY5bqmID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZpc2Nvc2l0eSIgTmFtZT0idmlzY29zaXR5X2R5bmFtaWMiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMzAxIiBVbml0PSJQYcK3cyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEzMDIiIFVuaXQ9Im1QYcK3cyIgaz0iMC4wMDEiIERpZ2l0PSI0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTEzMDMiIFVuaXQ9ImNQIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTMwNCIgVW5pdD0iUCIgaz0iMC4xIiBEaWdpdD0iNCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMzA1IiBVbml0PSJrZ2bCt3MvbcKyIiBrPSI5LjgwNjY1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxMzA2IiBVbml0PSJsYmbCt3MvZnTCsiIgaz0iNDcuODgwMyIgRGlnaXQ9IjMiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxNOe7hO+8mui/kOWKqOeymOW6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHlfa2luZW1hdGljIiBOYW1lPSJ2aXNjb3NpdHlfa2luZW1hdGljIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTQwMSIgVW5pdD0ibcKyL3MiIGs9IjEiIERpZ2l0PSI2IiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDAyIiBVbml0PSJtbcKyL3MiIGs9IjFlLTA2IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDAzIiBVbml0PSJjU3QiIGs9IjFlLTA2IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDA0IiBVbml0PSJTdCIgaz0iMC4wMDAxIiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNDA1IiBVbml0PSJjbcKyL3MiIGs9IjAuMDAwMSIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTQwNiIgVW5pdD0iZnTCsi9zIiBrPSIwLjA5MjkwMzA0IiBEaWdpdD0iNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDE157uE77ya5riX6YCP546HID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcm1lYWJpbGl0eSIgTmFtZT0icGVybWVhYmlsaXR5Ij4NCg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNTAxIiBVbml0PSJEYXJjeSIgaz0iMSIgRGlnaXQ9IjQiIFJlbWFyaz0iYmFzZSB1bml0Ii8+CiAgICAgICAgICAgIDxVbml0IElEPSJVMTUwMiIgVW5pdD0ibUQiIGs9IjAuMDAxIiBEaWdpdD0iNCIgLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTUwMyIgVW5pdD0iMS9jbcKyIiBrPSI5LjgxZSswNyIgRGlnaXQ9IjIiLz4NCg0KDQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMTbnu4TvvJrog73ph48gPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZW5lcmd5IiBOYW1lPSJlbmVyZ3kiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNjAxIiBVbml0PSJKIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTYwMiIgVW5pdD0ia0oiIGs9IjEwMDAiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDMiIFVuaXQ9Ik1KIiBrPSIxZTYiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDQiIFVuaXQ9ImNhbCIgaz0iNC4xODQiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDUiIFVuaXQ9ImtjYWwiIGs9IjQxODQiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDYiIFVuaXQ9IkJ0dSIgaz0iMTA1NS4wNTU4NTI2MiIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTYwNyIgVW5pdD0iQlRVIiBrPSIxMDU1LjA1NTg1MjYyIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNjA4IiBVbml0PSJrV8K3aCIgaz0iMy42ZTYiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MDkiIFVuaXQ9ImZ0wrdsYmYiIGs9IjEuMzU1ODE3OTQ4IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNjEwIiBVbml0PSJrZ2bCt20iIGs9IjkuODA2NjUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE2MTEiIFVuaXQ9ImhwwrdoIiBrPSIyLjY4NDUyZTYiIERpZ2l0PSIzIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMTfnu4TvvJrlip/njocgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icG93ZXIiIE5hbWU9InBvd2VyIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTcwMSIgVW5pdD0iVyIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE3MDIiIFVuaXQ9ImtXIiBrPSIxMDAwIiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNzAzIiBVbml0PSJNVyIgaz0iMWU2IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNzA0IiBVbml0PSJocCIgaz0iNzQ1LjY5OTg3MTU4MjI3IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxNzA1IiBVbml0PSJCdHUvaCIgaz0iMC4yOTMwNzEwNyIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTcwNiIgVW5pdD0iY2FsL3MiIGs9IjQuMTg2OCIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTcwNyIgVW5pdD0ia2dmwrdtL3MiIGs9IjkuODA2NjUiIERpZ2l0PSIzIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMTjnu4TvvJrkvZPnp6/mtYHph48gPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZmxvd19yYXRlX3ZvbHVtZSIgTmFtZT0iZmxvd19yYXRlX3ZvbHVtZSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE4MDEiIFVuaXQ9Im3Csy9kIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwMiIgVW5pdD0ibcKzL3MiIGs9Ijg2NDAwIiBEaWdpdD0iNiIgUmVtYXJrPSIxIG3Csy9zID0gODY0MDAgbcKzL2QiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwMyIgVW5pdD0ibcKzL2EiIGs9IjAuMDAyNzM5NzI2MDI3Mzk3MjYiIERpZ2l0PSI2IiBSZW1hcms9IjEgbcKzL2EgPSAxLzM2NSBtwrMvZCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxODA0IiBVbml0PSJzdGIvZCIgaz0iMSIgRGlnaXQ9IjYiIFJlbWFyaz0iMSBzdGIvZCDiiYggMSBtwrMvZCAo5bel56iL6L+R5Ly8KSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxODA1IiBVbml0PSJiYmwvZCIgaz0iMSIgRGlnaXQ9IjYiIFJlbWFyaz0iMSBiYmwvZCDiiYggMSBtwrMvZCAo5bel56iL6L+R5Ly8KSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxODA2IiBVbml0PSJmdMKzL3MiIGs9IjI0NDYuNTc1IiBEaWdpdD0iNiIgUmVtYXJrPSIxIGZ0wrMvcyA9IDI0NDYuNTc1IG3Csy9kIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE4MDciIFVuaXQ9ImZ0wrMvZCIgaz0iMC4wMjgzMTY4NDY1OTIiIERpZ2l0PSI2IiBSZW1hcms9IjEgZnTCsy9kID0gMC4wMjgzMTY4NDY1OTIgbcKzL2QiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwOCIgVW5pdD0iTU1zY2YvZCIgaz0iMjgzMTYuODQ2NTkyIiBEaWdpdD0iNCIgUmVtYXJrPSIxIE1Nc2NmL2QgPSAyODMxNi44NDY1OTIgbcKzL2QgKOeQhuaDs+awlOS9kykiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTgwOSIgVW5pdD0iTXNjZi9kIiBrPSIyOC4zMTY4NDY1OTIiIERpZ2l0PSI2IiBSZW1hcms9IjEgTXNjZi9kID0gMjguMzE2ODQ2NTkyIG3Csy9kICjnkIbmg7PmsJTkvZMpIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE4MTAiIFVuaXQ9IjEw4oG0Km3Csy9kIiBrPSIxMDAwMCIgRGlnaXQ9IjQiIFJlbWFyaz0iMSDkuIfmlrkv5aSpID0gMTAwMDAgbcKzL2QiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwxOee7hO+8muWOi+e8qeezu+aVsCA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJjb21wcmVzc2liaWxpdHkiIE5hbWU9ImNvbXByZXNzaWJpbGl0eSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE5MDEiIFVuaXQ9IjEvTVBhIiBrPSIxIiBEaWdpdD0iNiIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTkwMiIgVW5pdD0iMS9QYSIgaz0iMWU2IiBEaWdpdD0iNiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUxOTAzIiBVbml0PSIxL3BzaSIgaz0iMTQ1LjAzNzczOCIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMTkwNCIgVW5pdD0iMS9hdG0iIGs9IjkuODY5MjMyNjY3MTYiIERpZ2l0PSI2Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTE5MDUiIFVuaXQ9IjEvYmFyIiBrPSIxMCIgRGlnaXQ9IjYiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwyMOe7hO+8mua1k+W6piA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJjb25jZW50cmF0aW9uIiBOYW1lPSJjb25jZW50cmF0aW9uIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwMSIgVW5pdD0ia2cvbcKzIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwMiIgVW5pdD0iZy9MIiBrPSIxIiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMDAzIiBVbml0PSJtZy9MIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwNCIgVW5pdD0izrxnL21MIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjAwNSIgVW5pdD0icHBtIiBrPSIwLjAwMSIgRGlnaXQ9IjQiIFJlbWFyaz0iYXNzdW1pbmcgMSBwcG0gPSAxIG1nL2tnIGluIHdhdGVyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIwMDYiIFVuaXQ9IktwcG0iIGs9IjEiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIwMDciIFVuaXQ9Im1vbC9twrMiIGs9IjEiIERpZ2l0PSIzIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMjHnu4TvvJrnmb7liIbmr5QgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icGVyY2VudCIgTmFtZT0icGVyY2VudCI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIxMDEiIFVuaXQ9IiUiIGs9IjEiIERpZ2l0PSIyIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMTAyIiBVbml0PSJwdSIgaz0iMSIgRGlnaXQ9IjIiIFJlbWFyaz0icGVyIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjEwMyIgVW5pdD0iZnJhY3Rpb24iIGs9IjEwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjEwNCIgVW5pdD0idi92IiBrPSIxMDAiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTIxMDUiIFVuaXQ9Im3Csy9twrMiIGs9IjEwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwyMue7hO+8muaFouW6pu+8iOWjsOazou+8iSA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzbG93bmVzcyIgTmFtZT0ic2xvd25lc3MiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMjAxIiBVbml0PSJzL20iIGs9IjEiIERpZ2l0PSIzIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMjAyIiBVbml0PSJ1cy9tIiBrPSIxZS0wNiIgRGlnaXQ9IjYiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjIwMyIgVW5pdD0idXMvZnQiIGs9IjMuMjgwODM5ODk1MDEzMTJlLTA2IiBEaWdpdD0iNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDIz57uE77ya5pS+5bCE5oCnID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InJhZGlvYWN0aXZpdHkiIE5hbWU9InJhZGlvYWN0aXZpdHkiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMzAxIiBVbml0PSJBUEkiIGs9IjEiIERpZ2l0PSIxIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyMzAyIiBVbml0PSJnQVBJIiBrPSIxIiBEaWdpdD0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDI057uE77ya55S15Y6LID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZvbHRhZ2UiIE5hbWU9InZvbHRhZ2UiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNDAxIiBVbml0PSJWIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjQwMiIgVW5pdD0ibVYiIGs9IjAuMDAxIiBEaWdpdD0iNCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNDAzIiBVbml0PSJrViIgaz0iMTAwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwyNee7hO+8muWKmyA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJmb3JjZSIgTmFtZT0iZm9yY2UiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNTAxIiBVbml0PSJOIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjUwMiIgVW5pdD0ia04iIGs9IjEwMDAiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI1MDMiIFVuaXQ9ImxiZiIgaz0iNC40NDgyMjE2MTUyNjA1IiBEaWdpdD0iMyIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNTA0IiBVbml0PSJrZ2YiIGs9IjkuODA2NjUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI1MDUiIFVuaXQ9ImR5biIgaz0iMWUtMDUiIERpZ2l0PSI2Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMjbnu4TvvJrpopHnjocgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZnJlcXVlbmN5IiBOYW1lPSJmcmVxdWVuY3kiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyNjAxIiBVbml0PSJIeiIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI2MDIiIFVuaXQ9ImtIeiIgaz0iMTAwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjYwMyIgVW5pdD0iTUh6IiBrPSIxZTYiIERpZ2l0PSIyIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI2MDQiIFVuaXQ9IkdIeiIgaz0iMWU5IiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDI357uE77ya54Ot5a+8546HID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRoZXJtYWxfY29uZHVjdGl2aXR5IiBOYW1lPSJ0aGVybWFsX2NvbmR1Y3Rpdml0eSI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI3MDEiIFVuaXQ9IlcvKG3Ct0spIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjcwMiIgVW5pdD0iVy8obcK34oSDKSIgaz0iMSIgRGlnaXQ9IjMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjcwMyIgVW5pdD0iQnR1LyhowrdmdMK3wrBGKSIgaz0iMS43MzA3MzUiIERpZ2l0PSIzIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI3MDQiIFVuaXQ9ImNhbC8oY23Ct3PCt+KEgykiIGs9IjQxOC40IiBEaWdpdD0iMiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8IS0tID09PT09PT09PT09PT09PT09PT09IOesrDI457uE77ya5q+U54Ot5a65ID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InNwZWNpZmljX2hlYXQiIE5hbWU9InNwZWNpZmljX2hlYXQiPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyODAxIiBVbml0PSJKLyhrZ8K3SykiIGs9IjEiIERpZ2l0PSIzIiBSZW1hcms9ImJhc2UgdW5pdCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyODAyIiBVbml0PSJKLyhnwrdLKSIgaz0iMTAwMCIgRGlnaXQ9IjIiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjgwMyIgVW5pdD0iY2FsLyhnwrfihIMpIiBrPSI0MTg0IiBEaWdpdD0iMiIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUyODA0IiBVbml0PSJCdHUvKGxiwrfCsEYpIiBrPSI0MTg2LjgiIERpZ2l0PSIyIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMjnnu4TvvJrmkanlsJTotKjph48gPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibW9sYXJfbWFzcyIgTmFtZT0ibW9sYXJfbWFzcyI+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI5MDEiIFVuaXQ9ImtnL21vbCIgaz0iMSIgRGlnaXQ9IjMiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTI5MDIiIFVuaXQ9ImcvbW9sIiBrPSIwLjAwMSIgRGlnaXQ9IjQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMjkwMyIgVW5pdD0ibGIvbGJtb2wiIGs9IjAuNDUzNTkyMzciIERpZ2l0PSI0Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMzDnu4TvvJpVbmtub3duMSA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJtb2xhcl9tYXNzIiBOYW1lPSJtb2xhcl9tYXNzIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzAwMSIgVW5pdD0ia2cvbW9sIiBrPSIxIiBEaWdpdD0iMyIgUmVtYXJrPSJiYXNlIHVuaXQiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzAwMiIgVW5pdD0iZy9tb2wiIGs9IjAuMDAxIiBEaWdpdD0iNCIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUzMDAzIiBVbml0PSJsYi9sYm1vbCIgaz0iMC40NTM1OTIzNyIgRGlnaXQ9IjQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMzHnu4TvvJpVbmtub3duMSA9PT09PT09PT09PT09PT09PT09PSAtLT4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ3cyIgTmFtZT0i55uQ5bqmIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzEwMSIgVW5pdD0ibWcvTCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUzMTAyIiBVbml0PSLOvGcvbUwiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzEwMyIgVW5pdD0iZy9MIiBrPSIxMDAwIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTMxMDQiIFVuaXQ9ImtnL21eMyIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzEwNSIgVW5pdD0icHBtIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTMxMDYiIFVuaXQ9IktwcG0iIGs9IjEwMDAiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCiAgICAgICAgPCEtLSA9PT09PT09PT09PT09PT09PT09PSDnrKwzMue7hO+8mlVua25vd24xID09PT09PT09PT09PT09PT09PT09IC0tPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InZpc2Nvc2l0eSIgTmFtZT0idmlzY29zaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwMSIgVW5pdD0iUGEucyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUzMjAyIiBVbml0PSJtUGEucyIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwMyIgVW5pdD0iUCIgaz0iMC4xIi8+DQogICAgICAgICAgICA8VW5pdCBJRD0iVTMyMDQiIFVuaXQ9ImNQIiBrPSIwLjAwMSIvPg0KICAgICAgICAgICAgPFVuaXQgSUQ9IlUzMjA1IiBVbml0PSJiZi5zL2Z0XjIiIGs9IjQ3Ljg4MDMiLz4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzIwNiIgVW5pdD0ia2dmLnMvbV4zIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0g56ysMzPnu4TvvJrkupXnrZLlgqjpm4bns7vmlbAgPT09PT09PT09PT09PT09PT09PT0gLS0+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzMiIE5hbWU9IlRvRG8zIj4NCiAgICAgICAgICAgIDxVbml0IElEPSJVMzMwMSIgVW5pdD0ibV4zL01QYSIgaz0iMSIgRGlnaXQ9IjYiIFJlbWFyaz0iYmFzZSB1bml0Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgPC9UeXBlT2Y+DQo8L1Jvb3Q+DQo=
\ No newline at end of file
diff --git a/Bin/Config/Common/UnitDefault_en.dat b/Bin/Config/Common/UnitDefault_en.dat
index 1d2140c..a988b43 100644
--- a/Bin/Config/Common/UnitDefault_en.dat
+++ b/Bin/Config/Common/UnitDefault_en.dat
@@ -1 +1 @@
-77u/PD94bWwgcup_mpadmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxSb290Pg0KICAgIDxUeXwzyBlT2Y+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iY29tcHJlc3NpYmlsaXR5IiBOYW1lPSJjb21wcmVzc2liaWxpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL01QYSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL1BhIiBrPSIxZTYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9wc2kiIGs9IjE0NS4wMzgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9hdG0iIGs9IjkuODY5MjMiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZGVuc2l0eSIgTmFtZT0iZGVuc2l0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY21eMyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy9tXjMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImxiL2Z0XjMiIGs9IjAuMDE2MDIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVTKSIgaz0iMC4xMTk4MjYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVLKSIgaz0iMC4wOTk3NiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYi9iYmwiIGs9IjAuMDAyODUzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY2MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9jZiIgaz0iMy41MzE0N2UtMDUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9mdF4zIiBrPSIzLjUzMTQ3ZS0wNSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJnZW90aGVybWFsZ3JhZGllbnQiIE5hbWU9Imdlb3RoZXJtYWxncmFkaWVudCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImRlZy9tIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvaW4iIGs9IjEuOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkZWcva20iIGs9IjAuMDk5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvbWlsZSIgaz0iMC4wMzQ0ODI4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvZnQiIGs9IjE4MS44MTgiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibGVuZ3RoIiBOYW1lPSJsZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJpbiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtIiBrPSIzOS4zNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtIiBrPSIwLjM5MzcwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbSIgaz0iMC4wMzkzNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImttIiBrPSIzOTM3MC4xIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0IiBrPSIxMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtaSIgaz0iNjMzNDYuNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ5ZCIgaz0iMzUuOTg3MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubWlsZSIgaz0iNzI5MTMuNCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubSIgaz0iMy45MzcwMWUtMDgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0icmFkIiBrPSI3LjgyODMxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcmNlbnQiIE5hbWU9InBlcmNlbnQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIlIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InB1IiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9tXjMiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ2L3YiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmcmFjdGlvbiIgaz0iMTAwIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcm1lYWJpbGl0eSIgTmFtZT0icGVybWVhYmlsaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iRCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtRCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eLTIiIGs9IjkuODFlKzA3Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBvd2VyIiBOYW1lPSJwb3dlciI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlciIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iQnR1L2giIGs9IjAuMjkzMDcxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5tL3MiIGs9IjkuODA2NjUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY2FsL3MiIGs9IjQuMTg2OCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJocCIgaz0iNzM1LjQ5OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcmVzc3VyZSIgTmFtZT0icHJlc3N1cmUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJQYSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJNUGEiIGs9IjFlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IktQYSIgaz0iMTAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiYXIiIGs9IjEwMDAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJUb3JyIiBrPSIxMzMuMzIyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImR5bi9jbV4yIiBrPSIwLjEiLz4NCiAgICAgICAgICAgIDxVbml0IFVuaXQ9Im1tSGciIGs9IjEzMy4zMjIiLz4NCiAgICAgICAgICAgIDxVbml0IFVuaXQ9Im1tSDJPIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImF0IiBrPSI5ODA2Ni41Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InBzaSIgaz0iNjg5Ni41NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJhdG0iIGs9IjEwMTMyNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YvY21eMiIgaz0iOTgwNzkuNyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcm9kdWN0aW9uIiBOYW1lPSJwcm9kdWN0aW9uIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2QiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idGNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2EiIGs9IjAuMDAyNzQwMDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1jZi9kIiBrPSIyODMyMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtY2YvZCIgaz0iMjguMzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ic3RiL2QiIGs9IjAuMTU5Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InByb2RnYXMiIE5hbWU9InByb2RnYXMiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvZCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxMF40Km1eMy9kIiBrPSIxMDAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvYSIgaz0iMC4wMDI3NDAwNyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiY2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbWNmL2QiIGs9IjI4MzIwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1jZi9kIiBrPSIyOC4zMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJzdGIvZCIgaz0iMC4xNTkiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icHJvZG9pbCIgTmFtZT0icHJvZG9pbCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9kIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InRjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9hIiBrPSIwLjAwMjc0MDA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YvZCIgaz0iMjgzMjAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmL2QiIGs9IjI4LjMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0Yi9kIiBrPSIwLjE1OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcm9kd2F0ZXIiIE5hbWU9InByb2R3YXRlciI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9kIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InRjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9hIiBrPSIwLjAwMjc0MDA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YvZCIgaz0iMjgzMjAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmL2QiIGs9IjI4LjMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0Yi9kIiBrPSIwLjE1OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJyYWRpb2FjdGl2aXR5IiBOYW1lPSJyYWRpb2FjdGl2aXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZ0FQSSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJBUEkiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ic2xvd25lc3MiIE5hbWU9InNsb3duZXNzIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idXMvbSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ1cy9mdCIgaz0iMy4yODA4NCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzcXVhcmUiIE5hbWU9InNxdWFyZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrbV4yIiBrPSIxZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJpbl4yIiBrPSIwLjAwMDY0NTIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eMiIgaz0iMC4wMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImFjcmUiIGs9IjQwNDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWlsZV4yIiBrPSIyLjU4ODg4ZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmdF4yIiBrPSIwLjA5MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJoYSIgaz0iMTAwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ieWReMiIgaz0iMC44MzYxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InJhZDIiIGs9IjAuMDM5NTM2OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzdHJlbmd0aCIgTmFtZT0ic3RyZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJOIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImxiZiIgaz0iNC40NDQ0NCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YiIGs9IjkuODEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZHluIiBrPSIxZS0wNSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ0ZW1wZXJhdHVyZSIgTmFtZT0idGVtcGVyYXR1cmUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkZWciIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iSyIgaz0iMSIgYj0iMjczLjE1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYiIGs9IjEuOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSLihIMiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idGhlbWFsIHBvd2VyIiBOYW1lPSJ0aGVtYWwgcG93ZXIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJKIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNhbCIgaz0iNC4xODY4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5tIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkJ0dSIgaz0iMTA1NS4wNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrVy5oIiBrPSIzLjZlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0LmxiZiIgaz0iMS4zNTU4MiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJocC5oKFVTKSIgaz0iMi42NDc3OWUrMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iSHAuaChVSykiIGs9IjIuNjg0NTJlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnLm0iIGs9IjkuODAwMDgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia2NhbCIgaz0iNDE4NS44NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJCVFUiIGs9IjEwNTQuODUiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idGltZSIgTmFtZT0idGltZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InVzIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1zIiBrPSIxMDAwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InMiIGs9IjFlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1pbiIgaz0iNmUrMDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaHIiIGs9IjMuNmUrMDkiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iRGF5IiBrPSI4LjY0ZSsxMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2ZWxvY2l0eSIgTmFtZT0idmVsb2NpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtL3MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWlsZS9oIiBrPSIwLjQ0NzA0Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0L3MiIGs9IjAuMzA0OCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHkiIE5hbWU9InZpc2Nvc2l0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlBhLnMiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibVBhLnMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlAiIGs9IjAuMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJjUCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmYucy9mdF4yIiBrPSI0Ny44ODAzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5zL21eMyIgaz0iOS44MDY2NSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHkgYWN0IiBOYW1lPSJ2aXNjb3NpdHkgYWN0Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4yL3MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eMi9zIiBrPSIwLjAwMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZnReMi9zIiBrPSIwLjA5MjkwMyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJTdCIgaz0iMC4wMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNTdCIgaz0iMWUtMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1eMi9zIiBrPSIxZS0wNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2b2x0YWdlIiBOYW1lPSJ2b2x0YWdlIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibXYiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idiIgaz0iMTAwMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2b2x1bWUiIE5hbWU9InZvbHVtZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbCIgaz0iMWUtMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaW5eMyIgaz0iMS42MzllLTA1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtXjMiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InB0IiBrPSIwLjAwMDQ3MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnYWwoVVMpIiBrPSIwLjAwMzc4NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiYmwiIGs9IjAuMTU5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZiIgaz0iMi44MzE3ZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YiIGs9IjIuODMxN2UrMTAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1jZiIgaz0iMjgzMTciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmIiBrPSIyOC4zMTciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZnReMyIgaz0iMC4wMjgzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImdhbChVSykiIGs9IjAuMDA0NTQ2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImdpIiBrPSIwLjAwMDExOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJxdCIgaz0iMC4wMDA5NDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY2MiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImFjcmUuZnQiIGs9IjEyMzQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0id2VpZ2h0IiBOYW1lPSJ3ZWlnaHQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0b24iIGs9IjEwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibHRvbiIgaz0iMTAxNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYiIgaz0iMC40NTQiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ib3oiIGs9IjAuMDI4MzUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ic3RvbiIgaz0iOTA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImciIGs9IjAuMDAxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0id3MiIE5hbWU9IuebkOW6piI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1nL0wiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0izrxnL21MIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvTCIgaz0iMTAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy9tXjMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InBwbSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJLcHBtIiBrPSIxMDAwIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzEiIE5hbWU9IlRvRG8xIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9tXjMvZCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvMiIgTmFtZT0iVG9EbzIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSLCsCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvMyIgTmFtZT0iVG9EbzMiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvTVBhIiBrPSIxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IlRvRG80IiBOYW1lPSJUb0RvNCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1ELyhtUGEucykiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzUiIE5hbWU9IlRvRG81Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTVBhL20iIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzYiIE5hbWU9IlRvRG82Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibUQubSIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvNyIgTmFtZT0iVG9EbzciPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnL21vbCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KDQogICAgPC9UeXBlT2Y+DQo8L1Jvb3Q+DQo=
\ No newline at end of file
+77u/PD94bWwgcup_mpadmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxSb290Pg0KICAgIDxUeXwzyBlT2Y+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iY29tcHJlc3NpYmlsaXR5IiBOYW1lPSJjb21wcmVzc2liaWxpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL01QYSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL1BhIiBrPSIxZTYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9wc2kiIGs9IjE0NS4wMzgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9hdG0iIGs9IjkuODY5MjMiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZGVuc2l0eSIgTmFtZT0iZGVuc2l0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY21eMyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy9tXjMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImxiL2Z0XjMiIGs9IjAuMDE2MDIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVTKSIgaz0iMC4xMTk4MjYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVLKSIgaz0iMC4wOTk3NiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYi9iYmwiIGs9IjAuMDAyODUzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY2MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9jZiIgaz0iMy41MzE0N2UtMDUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9mdF4zIiBrPSIzLjUzMTQ3ZS0wNSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJnZW90aGVybWFsZ3JhZGllbnQiIE5hbWU9Imdlb3RoZXJtYWxncmFkaWVudCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImRlZy9tIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvaW4iIGs9IjEuOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkZWcva20iIGs9IjAuMDk5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvbWlsZSIgaz0iMC4wMzQ0ODI4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvZnQiIGs9IjE4MS44MTgiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibGVuZ3RoIiBOYW1lPSJsZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJpbiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtIiBrPSIzOS4zNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtIiBrPSIwLjM5MzcwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbSIgaz0iMC4wMzkzNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImttIiBrPSIzOTM3MC4xIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0IiBrPSIxMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtaSIgaz0iNjMzNDYuNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ5ZCIgaz0iMzUuOTg3MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubWlsZSIgaz0iNzI5MTMuNCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubSIgaz0iMy45MzcwMWUtMDgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0icmFkIiBrPSI3LjgyODMxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcmNlbnQiIE5hbWU9InBlcmNlbnQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIlIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InB1IiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9tXjMiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ2L3YiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmcmFjdGlvbiIgaz0iMTAwIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcm1lYWJpbGl0eSIgTmFtZT0icGVybWVhYmlsaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iRGFyY3kiIGs9IjEiLz4KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtRCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eLTIiIGs9IjkuODFlKzA3Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBvd2VyIiBOYW1lPSJwb3dlciI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlciIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iQnR1L2giIGs9IjAuMjkzMDcxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5tL3MiIGs9IjkuODA2NjUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY2FsL3MiIGs9IjQuMTg2OCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJocCIgaz0iNzM1LjQ5OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcmVzc3VyZSIgTmFtZT0icHJlc3N1cmUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJQYSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJNUGEiIGs9IjFlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IktQYSIgaz0iMTAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiYXIiIGs9IjEwMDAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJUb3JyIiBrPSIxMzMuMzIyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImR5bi9jbV4yIiBrPSIwLjEiLz4NCiAgICAgICAgICAgIDxVbml0IFVuaXQ9Im1tSGciIGs9IjEzMy4zMjIiLz4NCiAgICAgICAgICAgIDxVbml0IFVuaXQ9Im1tSDJPIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImF0IiBrPSI5ODA2Ni41Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InBzaSIgaz0iNjg5Ni41NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJhdG0iIGs9IjEwMTMyNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YvY21eMiIgaz0iOTgwNzkuNyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcm9kdWN0aW9uIiBOYW1lPSJwcm9kdWN0aW9uIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2QiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idGNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2EiIGs9IjAuMDAyNzQwMDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1jZi9kIiBrPSIyODMyMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtY2YvZCIgaz0iMjguMzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ic3RiL2QiIGs9IjAuMTU5Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InByb2RnYXMiIE5hbWU9InByb2RnYXMiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvZCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxMF40Km1eMy9kIiBrPSIxMDAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvYSIgaz0iMC4wMDI3NDAwNyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiY2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbWNmL2QiIGs9IjI4MzIwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1jZi9kIiBrPSIyOC4zMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJzdGIvZCIgaz0iMC4xNTkiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icHJvZG9pbCIgTmFtZT0icHJvZG9pbCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9kIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InRjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9hIiBrPSIwLjAwMjc0MDA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YvZCIgaz0iMjgzMjAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmL2QiIGs9IjI4LjMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0Yi9kIiBrPSIwLjE1OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcm9kd2F0ZXIiIE5hbWU9InByb2R3YXRlciI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9kIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InRjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9hIiBrPSIwLjAwMjc0MDA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YvZCIgaz0iMjgzMjAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmL2QiIGs9IjI4LjMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0Yi9kIiBrPSIwLjE1OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJyYWRpb2FjdGl2aXR5IiBOYW1lPSJyYWRpb2FjdGl2aXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZ0FQSSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJBUEkiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ic2xvd25lc3MiIE5hbWU9InNsb3duZXNzIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idXMvbSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ1cy9mdCIgaz0iMy4yODA4NCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzcXVhcmUiIE5hbWU9InNxdWFyZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrbV4yIiBrPSIxZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJpbl4yIiBrPSIwLjAwMDY0NTIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eMiIgaz0iMC4wMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImFjcmUiIGs9IjQwNDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWlsZV4yIiBrPSIyLjU4ODg4ZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmdF4yIiBrPSIwLjA5MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJoYSIgaz0iMTAwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ieWReMiIgaz0iMC44MzYxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InJhZDIiIGs9IjAuMDM5NTM2OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzdHJlbmd0aCIgTmFtZT0ic3RyZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJOIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImxiZiIgaz0iNC40NDQ0NCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YiIGs9IjkuODEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZHluIiBrPSIxZS0wNSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ0ZW1wZXJhdHVyZSIgTmFtZT0idGVtcGVyYXR1cmUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkZWciIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iSyIgaz0iMSIgYj0iMjczLjE1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYiIGs9IjEuOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSLihIMiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idGhlbWFsIHBvd2VyIiBOYW1lPSJ0aGVtYWwgcG93ZXIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJKIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNhbCIgaz0iNC4xODY4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5tIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkJ0dSIgaz0iMTA1NS4wNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrVy5oIiBrPSIzLjZlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0LmxiZiIgaz0iMS4zNTU4MiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJocC5oKFVTKSIgaz0iMi42NDc3OWUrMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iSHAuaChVSykiIGs9IjIuNjg0NTJlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnLm0iIGs9IjkuODAwMDgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia2NhbCIgaz0iNDE4NS44NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJCVFUiIGs9IjEwNTQuODUiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idGltZSIgTmFtZT0idGltZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InVzIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1zIiBrPSIxMDAwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InMiIGs9IjFlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1pbiIgaz0iNmUrMDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaHIiIGs9IjMuNmUrMDkiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iRGF5IiBrPSI4LjY0ZSsxMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2ZWxvY2l0eSIgTmFtZT0idmVsb2NpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtL3MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWlsZS9oIiBrPSIwLjQ0NzA0Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0L3MiIGs9IjAuMzA0OCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHkiIE5hbWU9InZpc2Nvc2l0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlBhLnMiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibVBhLnMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlAiIGs9IjAuMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJjUCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmYucy9mdF4yIiBrPSI0Ny44ODAzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5zL21eMyIgaz0iOS44MDY2NSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHkgYWN0IiBOYW1lPSJ2aXNjb3NpdHkgYWN0Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4yL3MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eMi9zIiBrPSIwLjAwMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZnReMi9zIiBrPSIwLjA5MjkwMyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJTdCIgaz0iMC4wMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNTdCIgaz0iMWUtMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1eMi9zIiBrPSIxZS0wNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2b2x0YWdlIiBOYW1lPSJ2b2x0YWdlIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibXYiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idiIgaz0iMTAwMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2b2x1bWUiIE5hbWU9InZvbHVtZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbCIgaz0iMWUtMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaW5eMyIgaz0iMS42MzllLTA1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtXjMiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InB0IiBrPSIwLjAwMDQ3MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnYWwoVVMpIiBrPSIwLjAwMzc4NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiYmwiIGs9IjAuMTU5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZiIgaz0iMi44MzE3ZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YiIGs9IjIuODMxN2UrMTAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1jZiIgaz0iMjgzMTciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmIiBrPSIyOC4zMTciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZnReMyIgaz0iMC4wMjgzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImdhbChVSykiIGs9IjAuMDA0NTQ2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImdpIiBrPSIwLjAwMDExOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJxdCIgaz0iMC4wMDA5NDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY2MiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImFjcmUuZnQiIGs9IjEyMzQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0id2VpZ2h0IiBOYW1lPSJ3ZWlnaHQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0b24iIGs9IjEwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibHRvbiIgaz0iMTAxNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYiIgaz0iMC40NTQiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ib3oiIGs9IjAuMDI4MzUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ic3RvbiIgaz0iOTA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImciIGs9IjAuMDAxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0id3MiIE5hbWU9IuebkOW6piI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1nL0wiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0izrxnL21MIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvTCIgaz0iMTAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy9tXjMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InBwbSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJLcHBtIiBrPSIxMDAwIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzEiIE5hbWU9IlRvRG8xIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9tXjMvZCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvMiIgTmFtZT0iVG9EbzIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSLCsCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvMyIgTmFtZT0iVG9EbzMiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvTVBhIiBrPSIxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IlRvRG80IiBOYW1lPSJUb0RvNCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1ELyhtUGEucykiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzUiIE5hbWU9IlRvRG81Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTVBhL20iIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzYiIE5hbWU9IlRvRG82Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibUQubSIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvNyIgTmFtZT0iVG9EbzciPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnL21vbCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KDQogICAgPC9UeXBlT2Y+DQo8L1Jvb3Q+DQo=
\ No newline at end of file
diff --git a/Bin/Config/Lang/cn/MPA_cn.qm b/Bin/Config/Lang/cn/MPA_cn.qm
index 496f6ae..3d5457b 100644
Binary files a/Bin/Config/Lang/cn/MPA_cn.qm and b/Bin/Config/Lang/cn/MPA_cn.qm differ
diff --git a/Bin/Config/Lang/cn/MPA_cn.ts b/Bin/Config/Lang/cn/MPA_cn.ts
index c9930a5..970e255 100644
--- a/Bin/Config/Lang/cn/MPA_cn.ts
+++ b/Bin/Config/Lang/cn/MPA_cn.ts
@@ -2129,12 +2129,6 @@
Normalized Gauss Newton
规则化高斯-牛顿
-
-
-
- Genetic Algorithm
- 遗传算法
-
Parameter
diff --git a/Bin/Config/Lang/cn/WTAI_cn.ts b/Bin/Config/Lang/cn/WTAI_cn.ts
index 2fd73d5..46e180b 100644
--- a/Bin/Config/Lang/cn/WTAI_cn.ts
+++ b/Bin/Config/Lang/cn/WTAI_cn.ts
@@ -4715,10 +4715,6 @@ MethodID:%1
Normalized Gauss Newton
规则化高斯-牛顿
-
- Genetic Algorithm
- 遗传算法
-
Parameter
diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm
index 32bc151..9df8a26 100644
Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ
diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts
index e5a7886..efd37e2 100644
--- a/Bin/Config/Lang/cn/nmNum_cn.ts
+++ b/Bin/Config/Lang/cn/nmNum_cn.ts
@@ -48,346 +48,19 @@ Reason: %1
- nmCalculationAutoFitGA
-
- === GA Automatic Fitting Started ===
- === GA自动拟合开始 ===
-
-
- Algorithm: Genetic Algorithm
- 算法:遗传算法
-
-
- ERROR: Failed to load configuration from data manager
- 错误:从数据管理器加载配置失败
-
-
- Enabled parameters count: %1
- 启用参数数量:%1
-
-
- ERROR: No parameters enabled for optimization
- 错误:没有启用优化参数
-
-
- ERROR: Target LogLog data is empty or insufficient
- 错误:目标双对数数据为空或不足
-
-
- ERROR: Target LogLog data arrays have inconsistent sizes
- 错误:目标双对数数据数组大小不一致
-
-
- Target data validation passed (%1 data points)
- 目标数据验证通过(%1 个数据点)
-
-
- === Evaluating Initial Solution (Elite Protection) ===
- === 评估初始解 ===
-
-
- Initial parameters:
- 初始参数:
-
-
- Starting initial solution evaluation...
- 开始初始解评估...
-
-
- ERROR: m_userInitialSolution is empty!
- 错误:初始解为空!
-
-
- Initial param[%1] = %2
- 初始参数[%1] = %2
-
-
- evaluateGenes returned: %1
- 评估基因 返回:%1
-
-
- Taking SUCCESS branch (fitness < 1e9)
- 进入成功分支
-
-
- Initial solution evaluation successful
- 初始解评估成功
-
-
- Initial fitness (error): %1
- 初始误差:%1
-
-
- Elite protection activated - initial solution will be preserved if no significant improvement found
- 如果未找到显著改进,将保留初始解.
-
-
- Taking FAILURE branch (fitness >= 1e9)
- 进入失败分支
-
-
- Initial solution evaluation failed - starting with random initialization
- 初始解评估失败 - 使用随机初始化开始
-
-
- Exception during initial solution evaluation
- 初始解评估期间出现异常
-
-
- Population initialized: %1 individuals, %2 dimensions
- 种群初始化:%1 个个体,%2 个维度
-
-
- === Starting GA Main Loop ===
- === 开始GA主循环 ===
-
-
- --- Generation %1/%2 ---
- --- 第 %1/%2 代 ---
-
-
- Current best error: %1
- 当前最佳误差:%1
-
-
- Total evaluations: %1 (successful: %2, failures: %3)
- 总评估次数:%1(成功:%2,失败:%3)
-
-
- Optimization stopped by user request
- 优化因用户请求而停止
-
-
- Generation %1 completed: best = %2, avg = %3, worst = %4
- 第 %1 代完成:最佳 = %2,平均 = %3,最差 = %4
-
-
- === TARGET ACHIEVED ===
- === 达到目标 ===
-
-
- Target error achieved! Current error: %1 < Target: %2
- 达到目标误差!当前误差:%1 < 目标:%2
-
-
- Optimization completed successfully after %1 generations
- 达到目标误差!当前误差:%1 < 目标:%2
-
-
- === TRUE CONVERGENCE DETECTED ===
- === 检测到真正收敛 ===
-
-
- Algorithm has converged to a stable solution
- 算法已收敛到稳定解
-
-
- Final error: %1 after %2 generations
- 最终误差:%1,经过 %2 代
-
-
- Solution quality: %1 (1.0 = target achieved)
- 解质量:%1
-
-
- === LOCAL OPTIMUM DETECTED ===
- === 检测到局部最优 ===
-
-
- Algorithm appears to be trapped in local optimum
- 算法似乎陷入局部最优
-
-
- Current error: %1 after %2 generations
- 当前误差:%1,经过 %2 代
-
-
- Suggestion: Try restarting with different parameters or larger search space
- 建议:尝试使用不同参数或更大搜索空间重新开始
-
-
- === CONSECUTIVE FAILURES ===
- === 连续失败 ===
-
-
- Too many consecutive failed generations (%1/%2)
- 连续失败代数过多(%1/%2)
-
-
- Optimization status: diversity=%1
- 优化状态:多样性=%1
-
-
- Generation %1 completed - Current best: %2
- 第 %1 代完成 - 当前最佳:%2
-
-
- CRITICAL ERROR: %1
- 严重错误: %1
-
-
- CRITICAL ERROR: Unknown exception in GA main loop
- 严重错误:GA主循环中的未知异常
-
-
- Applying optimized parameters to model...
- 正在将优化参数应用到模型...
-
-
- === Optimization Results ===
- === 优化结果 ===
-
-
- Final error: %1
- 最终误差: %1
-
-
- Total generations: %1
- 总代数:%1
-
-
- Total evaluations: %1 (successful: %2)
- 总评估次数: %1 (成功: %2)
-
-
- Optimized parameters:
- 优化参数:
-
-
- Parameters applied successfully to data manager
- 参数已成功应用到数据管理器
-
-
- ERROR: Failed to apply final parameters: %1
- 错误: 应用最终参数失败: %1
-
-
- ERROR: Unknown error applying final parameters
- 错误: 应用最终参数时出现未知错误
-
-
- === GA OPTIMIZATION SUCCESSFUL ===
- === GA优化成功 ===
-
-
- === GA OPTIMIZATION CONVERGED ===
- === GA优化收敛 ===
-
-
- === GA OPTIMIZATION - LOCAL OPTIMUM ===
- === GA优化 - 局部最优 ===
-
-
- === GA OPTIMIZATION - MAX GENERATIONS ===
- === GA优化 - 达到最大代数 ===
-
-
- === GA OPTIMIZATION STOPPED BY USER ===
- === GA优化 - 用户停止 ===
-
-
- === GA OPTIMIZATION FAILED ===
- === GA优化失败 ===
-
-
- === GA OPTIMIZATION - UNKNOWN END ===
- === GA优化 - 未知结束 ===
-
-
- Result: %1
- 结果: %1
-
-
- === User Stop Request Received ===
- === 用户停止请求已接收 ===
-
-
- Gracefully stopping GA optimization...
- 在停止GA优化...
-
-
- Force stopping current evaluation...
- 强制停止当前评估...
-
-
- GA optimization stop request processed
- GA优化停止请求已处理
-
-
- Stop request received but optimization is not running
- 收到停止请求但优化未运行
-
-
- Individual %1 improved: %2 -> %3
- 个体 %1 改进:%2 -> %3
-
-
- Individual %1: evaluation failed
- 个体 %1:评估失败
-
-
- Individual %1: Exception: %2
- 个体 %1:异常:%2
-
-
- Individual %1: Unknown exception
- 个体 %1:未知异常
-
-
- WARNING: No successful evaluations in generation %1 (consecutive failures: %2)
- 警告:第 %1 代中没有成功评估(连续失败:%2)
-
-
- Current generation stats: %1 successful, %2 failed out of %3 individuals (success rate: %4%)
- 当前代统计:%3 个个体中 %1 个成功,%2 个失败(成功率:%4%)
-
-
- ERROR: Too many consecutive failed generations (%1/%2) - stopping optimization
- 错误:连续失败代数过多(%1/%2)- 停止优化
-
-
- WARNING: Low success rate (%1%) in generation %2, but continuing optimization
- 警告:第 %2 代中成功率低(%1%),但继续优化
-
-
- No initial solution for elite protection
- 没有初始解用于迭代
-
-
- === Final Result Validation (Elite Protection) ===
- === 最终结果验证 ===
-
-
- Comparing results: Initial=%1, Final=%2
- 比较结果: 初始=%1, 最终=%2
-
-
- Improvement: %1 (%2%)
- 改进: %1 (%2%)
-
+ nmCalculationAutoFitPSO
- Elite protection triggered: insufficient improvement
- 改进不足
+ Effective improvement threshold: max(%1, %2% of baseline error); %3 consecutive ineffective steps trigger convergence confirmation
+ 有效改善阈值:取 %1 与基准误差的 %2% 中较大值;连续 %3 次无有效改善后进行收敛确认
- Threshold: %1%, Actual: %2%
- 阈值: %1%, 实际: %2%
+ No effective improvement for %1 consecutive steps; rebuilding sensitivity model for confirmation
+ 连续 %1 次无有效改善,正在重建灵敏度模型进行确认
- Restoring initial solution as final result
- 恢复初始解作为最终结果
+ Sensitivity rebuild produced no effective improvement; local convergence detected
+ 灵敏度重建后仍无有效改善,判定为局部收敛
-
- Initial solution restored successfully
- 初始解恢复成功
-
-
- Final result validated - significant improvement achieved
- 最终结果已验证 - 实现显著改进
-
-
-
- nmCalculationAutoFitPSO
=== User Stop Request Received ===
=== 用户停止请求已接收 ===
@@ -3698,6 +3371,38 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
Warning
警告
+
+ Invalid parameter range
+ 参数范围无效
+
+
+ The parameter table is unavailable.
+ 参数表不可用。
+
+
+ The parameter row is invalid.
+ 参数行无效。
+
+
+ The range values for %1 are incomplete.
+ %1 的范围值不完整。
+
+
+ The minimum value, initial value, and maximum value of %1 must be finite numbers.
+ %1 的最小值、初始值和最大值必须是有限数值。
+
+
+ The physical range of %1 is invalid.
+ %1 的物理范围无效。
+
+
+ The values of %1 exceed the physical range [%2, %3].
+ %1 的参数值超出物理范围 [%2, %3]。
+
+
+ The values of %1 must satisfy: minimum <= initial value <= maximum.
+ %1 的参数值必须满足:最小值 <= 初始值 <= 最大值。
+
Please select a target well!
请选择一口目标井!
@@ -3740,11 +3445,6 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
Optimized parameters have been applied to the model.
拟合参数已应用到模型。
-
- GA Optimization completed:
-
- GA求解完成:
-
Optimization Completed
拟合完成
@@ -3774,27 +3474,14 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
Optimization Stopped
拟合停止
-
- GA Optimization stopped by user:
-
- GA拟合被用户强行终止:
-
PSO algorithm selected.
用户选择PSO。
-
- GA algorithm selected.
- 用户选择GA
-
PSO (Particle Swarm)
PSO
-
- GA (Genetic Algorithm)
- GA
-
Darcy
@@ -3811,6 +3498,18 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
On
开启
+
+ Fracture conductivity
+ 裂缝导流能力
+
+
+ Fracture half length
+ 裂缝半长
+
+
+ The minimum value of %1 must be greater than zero for automatic fitting.
+ 自动拟合时,%1 的最小值和初始值必须大于零。
+
nmWxAutomaticfitting
diff --git a/Bin/Config/Unit/UnitDefault.dat b/Bin/Config/Unit/UnitDefault.dat
index 1d2140c..7637503 100644
--- a/Bin/Config/Unit/UnitDefault.dat
+++ b/Bin/Config/Unit/UnitDefault.dat
@@ -1 +1 @@
-77u/PD94bWwgcup_mpadmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxSb290Pg0KICAgIDxUeXwzyBlT2Y+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iY29tcHJlc3NpYmlsaXR5IiBOYW1lPSJjb21wcmVzc2liaWxpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL01QYSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL1BhIiBrPSIxZTYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9wc2kiIGs9IjE0NS4wMzgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9hdG0iIGs9IjkuODY5MjMiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZGVuc2l0eSIgTmFtZT0iZGVuc2l0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY21eMyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy9tXjMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImxiL2Z0XjMiIGs9IjAuMDE2MDIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVTKSIgaz0iMC4xMTk4MjYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVLKSIgaz0iMC4wOTk3NiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYi9iYmwiIGs9IjAuMDAyODUzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY2MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9jZiIgaz0iMy41MzE0N2UtMDUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9mdF4zIiBrPSIzLjUzMTQ3ZS0wNSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJnZW90aGVybWFsZ3JhZGllbnQiIE5hbWU9Imdlb3RoZXJtYWxncmFkaWVudCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImRlZy9tIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvaW4iIGs9IjEuOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkZWcva20iIGs9IjAuMDk5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvbWlsZSIgaz0iMC4wMzQ0ODI4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvZnQiIGs9IjE4MS44MTgiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibGVuZ3RoIiBOYW1lPSJsZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJpbiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtIiBrPSIzOS4zNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtIiBrPSIwLjM5MzcwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbSIgaz0iMC4wMzkzNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImttIiBrPSIzOTM3MC4xIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0IiBrPSIxMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtaSIgaz0iNjMzNDYuNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ5ZCIgaz0iMzUuOTg3MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubWlsZSIgaz0iNzI5MTMuNCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubSIgaz0iMy45MzcwMWUtMDgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0icmFkIiBrPSI3LjgyODMxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcmNlbnQiIE5hbWU9InBlcmNlbnQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIlIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InB1IiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9tXjMiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ2L3YiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmcmFjdGlvbiIgaz0iMTAwIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcm1lYWJpbGl0eSIgTmFtZT0icGVybWVhYmlsaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iRCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtRCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eLTIiIGs9IjkuODFlKzA3Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBvd2VyIiBOYW1lPSJwb3dlciI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlciIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iQnR1L2giIGs9IjAuMjkzMDcxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5tL3MiIGs9IjkuODA2NjUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY2FsL3MiIGs9IjQuMTg2OCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJocCIgaz0iNzM1LjQ5OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcmVzc3VyZSIgTmFtZT0icHJlc3N1cmUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJQYSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJNUGEiIGs9IjFlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IktQYSIgaz0iMTAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiYXIiIGs9IjEwMDAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJUb3JyIiBrPSIxMzMuMzIyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImR5bi9jbV4yIiBrPSIwLjEiLz4NCiAgICAgICAgICAgIDxVbml0IFVuaXQ9Im1tSGciIGs9IjEzMy4zMjIiLz4NCiAgICAgICAgICAgIDxVbml0IFVuaXQ9Im1tSDJPIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImF0IiBrPSI5ODA2Ni41Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InBzaSIgaz0iNjg5Ni41NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJhdG0iIGs9IjEwMTMyNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YvY21eMiIgaz0iOTgwNzkuNyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcm9kdWN0aW9uIiBOYW1lPSJwcm9kdWN0aW9uIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2QiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idGNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2EiIGs9IjAuMDAyNzQwMDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1jZi9kIiBrPSIyODMyMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtY2YvZCIgaz0iMjguMzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ic3RiL2QiIGs9IjAuMTU5Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InByb2RnYXMiIE5hbWU9InByb2RnYXMiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvZCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxMF40Km1eMy9kIiBrPSIxMDAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvYSIgaz0iMC4wMDI3NDAwNyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiY2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbWNmL2QiIGs9IjI4MzIwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1jZi9kIiBrPSIyOC4zMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJzdGIvZCIgaz0iMC4xNTkiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icHJvZG9pbCIgTmFtZT0icHJvZG9pbCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9kIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InRjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9hIiBrPSIwLjAwMjc0MDA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YvZCIgaz0iMjgzMjAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmL2QiIGs9IjI4LjMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0Yi9kIiBrPSIwLjE1OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcm9kd2F0ZXIiIE5hbWU9InByb2R3YXRlciI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9kIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InRjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9hIiBrPSIwLjAwMjc0MDA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YvZCIgaz0iMjgzMjAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmL2QiIGs9IjI4LjMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0Yi9kIiBrPSIwLjE1OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJyYWRpb2FjdGl2aXR5IiBOYW1lPSJyYWRpb2FjdGl2aXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZ0FQSSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJBUEkiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ic2xvd25lc3MiIE5hbWU9InNsb3duZXNzIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idXMvbSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ1cy9mdCIgaz0iMy4yODA4NCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzcXVhcmUiIE5hbWU9InNxdWFyZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrbV4yIiBrPSIxZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJpbl4yIiBrPSIwLjAwMDY0NTIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eMiIgaz0iMC4wMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImFjcmUiIGs9IjQwNDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWlsZV4yIiBrPSIyLjU4ODg4ZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmdF4yIiBrPSIwLjA5MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJoYSIgaz0iMTAwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ieWReMiIgaz0iMC44MzYxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InJhZDIiIGs9IjAuMDM5NTM2OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJzdHJlbmd0aCIgTmFtZT0ic3RyZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJOIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImxiZiIgaz0iNC40NDQ0NCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YiIGs9IjkuODEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZHluIiBrPSIxZS0wNSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ0ZW1wZXJhdHVyZSIgTmFtZT0idGVtcGVyYXR1cmUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkZWciIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iSyIgaz0iMSIgYj0iMjczLjE1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYiIGs9IjEuOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSLihIMiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idGhlbWFsIHBvd2VyIiBOYW1lPSJ0aGVtYWwgcG93ZXIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJKIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNhbCIgaz0iNC4xODY4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5tIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkJ0dSIgaz0iMTA1NS4wNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrVy5oIiBrPSIzLjZlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0LmxiZiIgaz0iMS4zNTU4MiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJocC5oKFVTKSIgaz0iMi42NDc3OWUrMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iSHAuaChVSykiIGs9IjIuNjg0NTJlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnLm0iIGs9IjkuODAwMDgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia2NhbCIgaz0iNDE4NS44NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJCVFUiIGs9IjEwNTQuODUiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idGltZSIgTmFtZT0idGltZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InVzIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1zIiBrPSIxMDAwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InMiIGs9IjFlKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1pbiIgaz0iNmUrMDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaHIiIGs9IjMuNmUrMDkiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iRGF5IiBrPSI4LjY0ZSsxMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2ZWxvY2l0eSIgTmFtZT0idmVsb2NpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtL3MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWlsZS9oIiBrPSIwLjQ0NzA0Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0L3MiIGs9IjAuMzA0OCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHkiIE5hbWU9InZpc2Nvc2l0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlBhLnMiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibVBhLnMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IlAiIGs9IjAuMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJjUCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmYucy9mdF4yIiBrPSI0Ny44ODAzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtnZi5zL21eMyIgaz0iOS44MDY2NSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2aXNjb3NpdHkgYWN0IiBOYW1lPSJ2aXNjb3NpdHkgYWN0Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4yL3MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY21eMi9zIiBrPSIwLjAwMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZnReMi9zIiBrPSIwLjA5MjkwMyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJTdCIgaz0iMC4wMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNTdCIgaz0iMWUtMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1eMi9zIiBrPSIxZS0wNiIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2b2x0YWdlIiBOYW1lPSJ2b2x0YWdlIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibXYiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idiIgaz0iMTAwMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJ2b2x1bWUiIE5hbWU9InZvbHVtZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbCIgaz0iMWUtMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTCIgaz0iMC4wMDEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaW5eMyIgaz0iMS42MzllLTA1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtXjMiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InB0IiBrPSIwLjAwMDQ3MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnYWwoVVMpIiBrPSIwLjAwMzc4NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiYmwiIGs9IjAuMTU5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZiIgaz0iMi44MzE3ZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YiIGs9IjIuODMxN2UrMTAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1jZiIgaz0iMjgzMTciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmIiBrPSIyOC4zMTciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZnReMyIgaz0iMC4wMjgzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImdhbChVSykiIGs9IjAuMDA0NTQ2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImdpIiBrPSIwLjAwMDExOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJxdCIgaz0iMC4wMDA5NDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY2MiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImFjcmUuZnQiIGs9IjEyMzQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0id2VpZ2h0IiBOYW1lPSJ3ZWlnaHQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0b24iIGs9IjEwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibHRvbiIgaz0iMTAxNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYiIgaz0iMC40NTQiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ib3oiIGs9IjAuMDI4MzUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ic3RvbiIgaz0iOTA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImciIGs9IjAuMDAxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0id3MiIE5hbWU9IuebkOW6piI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1nL0wiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0izrxnL21MIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvTCIgaz0iMTAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy9tXjMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InBwbSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJLcHBtIiBrPSIxMDAwIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KDQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzEiIE5hbWU9IlRvRG8xIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9tXjMvZCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvMiIgTmFtZT0iVG9EbzIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSLCsCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvMyIgTmFtZT0iVG9EbzMiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvTVBhIiBrPSIxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IlRvRG80IiBOYW1lPSJUb0RvNCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1ELyhtUGEucykiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzUiIE5hbWU9IlRvRG81Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTVBhL20iIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzYiIE5hbWU9IlRvRG82Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibUQubSIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvNyIgTmFtZT0iVG9EbzciPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnL21vbCIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KDQogICAgPC9UeXBlT2Y+DQo8L1Jvb3Q+DQo=
\ No newline at end of file
+77u/PD94bWwgcup_mpadmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxSb290Pg0KICAgIDxUeXwzyBlT2Y+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iY29tcHJlc3NpYmlsaXR5IiBOYW1lPSJjb21wcmVzc2liaWxpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL01QYSIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIxL1BhIiBrPSIxZTYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9wc2kiIGs9IjE0NS4wMzgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMS9hdG0iIGs9IjkuODY5MjMiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iZGVuc2l0eSIgTmFtZT0iZGVuc2l0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY21eMyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy9tXjMiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImxiL2Z0XjMiIGs9IjAuMDE2MDIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVTKSIgaz0iMC4xMTk4MjYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIvZ2FsKFVLKSIgaz0iMC4wOTk3NiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYi9iYmwiIGs9IjAuMDAyODUzIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImcvY2MiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9jZiIgaz0iMy41MzE0N2UtMDUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9mdF4zIiBrPSIzLjUzMTQ3ZS0wNSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJnZW90aGVybWFsZ3JhZGllbnQiIE5hbWU9Imdlb3RoZXJtYWxncmFkaWVudCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImRlZy9tIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvaW4iIGs9IjEuOCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkZWcva20iIGs9IjAuMDk5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvbWlsZSIgaz0iMC4wMzQ0ODI4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkYvZnQiIGs9IjE4MS44MTgiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ibGVuZ3RoIiBOYW1lPSJsZW5ndGgiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJpbiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtIiBrPSIzOS4zNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtIiBrPSIwLjM5MzcwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbSIgaz0iMC4wMzkzNzAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImttIiBrPSIzOTM3MC4xIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0IiBrPSIxMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtaSIgaz0iNjMzNDYuNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ5ZCIgaz0iMzUuOTg3MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubWlsZSIgaz0iNzI5MTMuNCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJubSIgaz0iMy45MzcwMWUtMDgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0icmFkIiBrPSI3LjgyODMxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcmNlbnQiIE5hbWU9InBlcmNlbnQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSIlIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InB1IiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9tXjMiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ2L3YiIGs9IjEwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmcmFjdGlvbiIgaz0iMTAwIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InBlcm1lYWJpbGl0eSIgTmFtZT0icGVybWVhYmlsaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iRGFyY3kiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibUQiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtXi0yIiBrPSI5LjgxZSswNyIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwb3dlciIgTmFtZT0icG93ZXIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJXIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkJ0dS9oIiBrPSIwLjI5MzA3MSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YubS9zIiBrPSI5LjgwNjY1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNhbC9zIiBrPSI0LjE4NjgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaHAiIGs9IjczNS40OTkiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icHJlc3N1cmUiIE5hbWU9InByZXNzdXJlIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iUGEiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTVBhIiBrPSIxZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJLUGEiIGs9IjEwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmFyIiBrPSIxMDAwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iVG9yciIgaz0iMTMzLjMyMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJkeW4vY21eMiIgaz0iMC4xIi8+DQogICAgICAgICAgICA8VW5pdCBVbml0PSJtbUhnIiBrPSIxMzMuMzIyIi8+DQogICAgICAgICAgICA8VW5pdCBVbml0PSJtbUgyTyIgaz0iOS44MDY2NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJhdCIgaz0iOTgwNjYuNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJwc2kiIGs9IjY4OTYuNTUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYXRtIiBrPSIxMDEzMjUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia2dmL2NtXjIiIGs9Ijk4MDc5LjciLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icHJvZHVjdGlvbiIgTmFtZT0icHJvZHVjdGlvbiI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9kIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InRjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMy9hIiBrPSIwLjAwMjc0MDA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJjZi9kIiBrPSIwLjAyODMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YvZCIgaz0iMjgzMjAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWNmL2QiIGs9IjI4LjMyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0Yi9kIiBrPSIwLjE1OSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJwcm9kZ2FzIiBOYW1lPSJwcm9kZ2FzIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2QiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iMTBeNCptXjMvZCIgaz0iMTAwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idGNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL2EiIGs9IjAuMDAyNzQwMDciLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmNmL2QiIGs9IjAuMDI4MzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibW1jZi9kIiBrPSIyODMyMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtY2YvZCIgaz0iMjguMzIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ic3RiL2QiIGs9IjAuMTU5Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InByb2RvaWwiIE5hbWU9InByb2RvaWwiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvZCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvYSIgaz0iMC4wMDI3NDAwNyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiY2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbWNmL2QiIGs9IjI4MzIwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1jZi9kIiBrPSIyOC4zMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJzdGIvZCIgaz0iMC4xNTkiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icHJvZHdhdGVyIiBOYW1lPSJwcm9kd2F0ZXIiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvZCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ0Y2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMvYSIgaz0iMC4wMDI3NDAwNyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiY2YvZCIgaz0iMC4wMjgzMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtbWNmL2QiIGs9IjI4MzIwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1jZi9kIiBrPSIyOC4zMiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJzdGIvZCIgaz0iMC4xNTkiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0icmFkaW9hY3Rpdml0eSIgTmFtZT0icmFkaW9hY3Rpdml0eSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImdBUEkiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iQVBJIiBrPSIxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InNsb3duZXNzIiBOYW1lPSJzbG93bmVzcyI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InVzL20iIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idXMvZnQiIGs9IjMuMjgwODQiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ic3F1YXJlIiBOYW1lPSJzcXVhcmUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjIiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia21eMiIgaz0iMWUrMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaW5eMiIgaz0iMC4wMDA2NDUyIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtXjIiIGs9IjAuMDAwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJhY3JlIiBrPSI0MDQ3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1pbGVeMiIgaz0iMi41ODg4OGUrMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZnReMiIgaz0iMC4wOTMiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaGEiIGs9IjEwMDAwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InlkXjIiIGs9IjAuODM2MSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJyYWQyIiBrPSIwLjAzOTUzNjkiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0ic3RyZW5ndGgiIE5hbWU9InN0cmVuZ3RoIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iTiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJsYmYiIGs9IjQuNDQ0NDQiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia2dmIiBrPSI5LjgxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImR5biIgaz0iMWUtMDUiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idGVtcGVyYXR1cmUiIE5hbWU9InRlbXBlcmF0dXJlIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZGVnIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IksiIGs9IjEiIGI9IjI3My4xNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJGIiBrPSIxLjgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0i4oSDIiBrPSIxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRoZW1hbCBwb3dlciIgTmFtZT0idGhlbWFsIHBvd2VyIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iSiIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJjYWwiIGs9IjQuMTg2OCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2YubSIgaz0iOS44MDY2NSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJCdHUiIGs9IjEwNTUuMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia1cuaCIgaz0iMy42ZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmdC5sYmYiIGs9IjEuMzU1ODIiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iaHAuaChVUykiIGs9IjIuNjQ3NzllKzA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkhwLmgoVUspIiBrPSIyLjY4NDUyZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZy5tIiBrPSI5LjgwMDA4Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImtjYWwiIGs9IjQxODUuODUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iQlRVIiBrPSIxMDU0Ljg1Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9InRpbWUiIE5hbWU9InRpbWUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJ1cyIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtcyIgaz0iMTAwMCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJzIiBrPSIxZSswNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtaW4iIGs9IjZlKzA3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImhyIiBrPSIzLjZlKzA5Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkRheSIgaz0iOC42NGUrMTAiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idmVsb2NpdHkiIE5hbWU9InZlbG9jaXR5Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibS9zIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1pbGUvaCIgaz0iMC40NDcwNCIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJmdC9zIiBrPSIwLjMwNDgiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idmlzY29zaXR5IiBOYW1lPSJ2aXNjb3NpdHkiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJQYS5zIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1QYS5zIiBrPSIwLjAwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJQIiBrPSIwLjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iY1AiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImJmLnMvZnReMiIgaz0iNDcuODgwMyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJrZ2Yucy9tXjMiIGs9IjkuODA2NjUiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idmlzY29zaXR5IGFjdCIgTmFtZT0idmlzY29zaXR5IGFjdCI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1eMi9zIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNtXjIvcyIgaz0iMC4wMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0XjIvcyIgaz0iMC4wOTI5MDMiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iU3QiIGs9IjAuMDAwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJjU3QiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tXjIvcyIgaz0iMWUtMDYiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idm9sdGFnZSIgTmFtZT0idm9sdGFnZSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im12IiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InYiIGs9IjEwMDAiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0idm9sdW1lIiBOYW1lPSJ2b2x1bWUiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtXjMiIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibWwiIGs9IjFlLTA2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IkwiIGs9IjAuMDAxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImluXjMiIGs9IjEuNjM5ZS0wNSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJjbV4zIiBrPSIxZS0wNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJwdCIgaz0iMC4wMDA0NzMiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZ2FsKFVTKSIgaz0iMC4wMDM3ODUiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iYmJsIiBrPSIwLjE1OSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJiY2YiIGs9IjIuODMxN2UrMDYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idGNmIiBrPSIyLjgzMTdlKzEwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1tY2YiIGs9IjI4MzE3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1jZiIgaz0iMjguMzE3Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImZ0XjMiIGs9IjAuMDI4MyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnYWwoVUspIiBrPSIwLjAwNDU0NiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnaSIgaz0iMC4wMDAxMTgiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0icXQiIGs9IjAuMDAwOTQ2Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9ImNjIiBrPSIxZS0wNiIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJhY3JlLmZ0IiBrPSIxMjM0Ii8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IndlaWdodCIgTmFtZT0id2VpZ2h0Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia2ciIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0idG9uIiBrPSIxMDAwIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Imx0b24iIGs9IjEwMTYiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibGIiIGs9IjAuNDU0Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im96IiBrPSIwLjAyODM1Ii8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9InN0b24iIGs9IjkwNyIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnIiBrPSIwLjAwMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IndzIiBOYW1lPSLnm5DluqYiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtZy9MIiBrPSIxIi8+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Is68Zy9tTCIgaz0iMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJnL0wiIGs9IjEwMDAiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ia2cvbV4zIiBrPSIwLjAwMSIvPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJwcG0iIGs9IjEiLz4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iS3BwbSIgaz0iMTAwMCIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IlRvRG8xIiBOYW1lPSJUb0RvMSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9IjEvbV4zL2QiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzIiIE5hbWU9IlRvRG8yIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iwrAiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzMiIE5hbWU9IlRvRG8zIj4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0ibV4zL01QYSIgaz0iMSIvPg0KICAgICAgICA8L1VuaXRHcm91cD4NCiAgICAgICAgPFVuaXRHcm91cCBUeXBlPSJUb0RvNCIgTmFtZT0iVG9EbzQiPg0KICAgICAgICAgICAgPFVuaXQgUmVtYXJrPSIgIiBVbml0PSJtRC8obVBhLnMpIiBrPSIxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IlRvRG81IiBOYW1lPSJUb0RvNSI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Ik1QYS9tIiBrPSIxIi8+DQogICAgICAgIDwvVW5pdEdyb3VwPg0KICAgICAgICA8VW5pdEdyb3VwIFR5cGU9IlRvRG82IiBOYW1lPSJUb0RvNiI+DQogICAgICAgICAgICA8VW5pdCBSZW1hcms9IiAiIFVuaXQ9Im1ELm0iIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQogICAgICAgIDxVbml0R3JvdXAgVHlwZT0iVG9EbzciIE5hbWU9IlRvRG83Ij4NCiAgICAgICAgICAgIDxVbml0IFJlbWFyaz0iICIgVW5pdD0iZy9tb2wiIGs9IjEiLz4NCiAgICAgICAgPC9Vbml0R3JvdXA+DQoNCg0KICAgIDwvVHlwZU9mPg0KPC9Sb290Pg0K
\ No newline at end of file
diff --git a/Bin/XmlFiles/ModelParaDefinesNM_cn.xml b/Bin/XmlFiles/ModelParaDefinesNM_cn.xml
index 367fb1c..f26f2ef 100644
--- a/Bin/XmlFiles/ModelParaDefinesNM_cn.xml
+++ b/Bin/XmlFiles/ModelParaDefinesNM_cn.xml
@@ -17,7 +17,7 @@
-
+
@@ -90,7 +90,7 @@
-
+
diff --git a/Bin/XmlFiles/ModelParaDefinesNM_en.xml b/Bin/XmlFiles/ModelParaDefinesNM_en.xml
index fb7e17f..ad27365 100644
--- a/Bin/XmlFiles/ModelParaDefinesNM_en.xml
+++ b/Bin/XmlFiles/ModelParaDefinesNM_en.xml
@@ -18,7 +18,7 @@
-
+
@@ -91,7 +91,7 @@
-
+
diff --git a/Bin/XmlFiles/UnitDefault_cn.xml b/Bin/XmlFiles/UnitDefault_cn.xml
index e96401b..28882e7 100644
--- a/Bin/XmlFiles/UnitDefault_cn.xml
+++ b/Bin/XmlFiles/UnitDefault_cn.xml
@@ -166,7 +166,7 @@
-
+
diff --git a/Bin/XmlFiles/UnitDefault_en.xml b/Bin/XmlFiles/UnitDefault_en.xml
index b6fa119..3c4111b 100644
--- a/Bin/XmlFiles/UnitDefault_en.xml
+++ b/Bin/XmlFiles/UnitDefault_en.xml
@@ -46,7 +46,7 @@
-
+
diff --git a/Include/mAlg/mAlgDefines/mAlgDefines.h b/Include/mAlg/mAlgDefines/mAlgDefines.h
index 7c96b83..4eeaa12 100644
--- a/Include/mAlg/mAlgDefines/mAlgDefines.h
+++ b/Include/mAlg/mAlgDefines/mAlgDefines.h
@@ -292,8 +292,7 @@ enum Fit_Method
{
FM_GaussNewton = 0, //高斯牛顿
FM_GaussNewtonEx, //归一化高斯牛顿
- FM_Genetic, //遗传算法
- FM_ParticleSwarm, //粒子群算法(Particle Swarm Optimization)
+ FM_ParticleSwarm = 3, //粒子群算法(Particle Swarm Optimization),保留原有枚举值
FM_Unknown
};
diff --git a/Include/nmNum/nmCalculation/nmCalculationAutoFitGA.h b/Include/nmNum/nmCalculation/nmCalculationAutoFitGA.h
deleted file mode 100644
index 64f9e4d..0000000
--- a/Include/nmNum/nmCalculation/nmCalculationAutoFitGA.h
+++ /dev/null
@@ -1,267 +0,0 @@
-#ifndef NMCALCULATIONAUTOFITGA_H
-#define NMCALCULATIONAUTOFITGA_H
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "nmCalculation_global.h"
-
-class nmDataWellBase;
-
-enum StopReasonGA {
- GA_CONTINUE_OPTIMIZATION = 0,
- GA_TARGET_ACHIEVED,
- GA_TRUE_CONVERGENCE,
- GA_LOCAL_OPTIMUM,
- GA_MAX_ITERATIONS,
- GA_USER_STOPPED,
- GA_CONSECUTIVE_FAILURES,
- GA_OPTIMIZATION_FAILED
-};
-
-struct GAIndividual
-{
- QVector genes; // 基因(参数值)
- double fitness; // 适应度值
- bool isEvaluated; // 是否已评估
-
- GAIndividual() : fitness(1e10), isEvaluated(false) {}
-};
-
-class NMCALCULATION_EXPORT nmCalculationAutoFitGA : public QObject
-{
- Q_OBJECT
-
-public:
- explicit nmCalculationAutoFitGA(QObject* parent = nullptr);
- virtual ~nmCalculationAutoFitGA();
-
- // ==================== 公共接口方法 ====================
- void setTargetLogLogData(const QVector>& targetData);
- bool startAutoFitting();
- void stopFitting();
- bool isRunning() const;
- int getCurrentGeneration() const;
- QVector getBestSolution() const;
- double getBestFitness() const;
- QString getLastError() const;
- void resetOptimizer();
-
- void setGATargetWellName(const QString& wellName);
-
-signals:
- void progressUpdated(int generation, double bestFitness);
- void fittingFinished(bool success, const QString& message);
- void logMessageGenerated(const QString& message);
-
- private slots:
- void updateProgress();
-
-private:
-
- // 临时目录管理
- void initializeTemporaryDirectory();
- void cleanupTemporaryDirectory();
- bool removeDirectoryRecursively(const QString& path);
-
- // ==================== 数据加载方法 ====================
- // 从数据管理器加载所有配置
- bool loadAllConfigFromDataManager();
- // 加载优化配置
- void loadOptimizationConfig();
- // 加载参数边界
- void loadParameterBounds();
- // 提取用户初始值
- void extractUserInitialValues();
- // ==================== 遗传算法核心方法 ====================
- // 初始化种群
- void initializePopulation();
- // 评估基因
- double evaluateGenes(const QVector& genes);
- // 评估个体
- double evaluateIndividual(GAIndividual& individual);
- // 评估种群
- void evaluatePopulation();
- // 选择操作
- int tournamentSelection();
- int rouletteWheelSelection();
-
- // 交叉操作
- void crossover(const GAIndividual& parent1, const GAIndividual& parent2,
- GAIndividual& offspring1, GAIndividual& offspring2);
- void singlePointCrossover(const GAIndividual& parent1, const GAIndividual& parent2,
- GAIndividual& offspring1, GAIndividual& offspring2);
- void uniformCrossover(const GAIndividual& parent1, const GAIndividual& parent2,
- GAIndividual& offspring1, GAIndividual& offspring2);
-
- // 变异操作
- void mutate(GAIndividual& individual);
- void gaussianMutation(GAIndividual& individual);
- void polynomialMutation(GAIndividual& individual);
-
- // 精英保留
- void applyElitism(QVector& newPopulation);
- // 更新种群统计
- void updatePopulationStatistics();
- // 收敛检查
- bool checkConvergence();
- // 自适应参数更新
- void adaptiveParameterUpdate(int generation);
-
- // ==================== 智能收敛判断方法 ====================
- // 分析优化状态
- StopReasonGA analyzeOptimizationStatus();
- // 检查真收敛
- bool checkTrueConvergence() const;
- // 检查局部最优陷阱
- bool checkLocalOptimumTrap() const;
- // 计算种群多样性
- double calculatePopulationDiversity() const;
- // 计算适应度方差
- double calculateFitnessVariance(int windowSize) const;
- // 计算长期改进
- double calculateLongTermImprovement(int windowSize) const;
- // 更新收敛指标
- void updateConvergenceMetrics();
- // 最终结果验证和保护
- void validateAndProtectFinalResult();
-
- // ==================== 参数处理方法 ====================
- // 参数验证
- bool validateParameters(const QVector& parameters) const;
- // 双对数数据验证
- bool validateLogLogData(const QVector>& logLogData) const;
- // 初始值验证
- bool validateInitialValues() const;
- // 应用参数到数据管理器
- void applyParametersToDataManager(const QVector& parameters);
- // 更新储层参数
- void updateReservoirParameters(const QVector& parameters);
- // 更新井参数
- void updateWellParameters(const QVector& parameters);
- // 更新井到数据管理器
- void updateWellToDataManager(nmDataWellBase* pWell);
- // 参数边界约束
- void clampToLimits(QVector& parameters) const;
-
- // ==================== 求解器相关方法 ====================
- // 运行求解器
- QVector> runSolver();
- // 运行EXE求解器
- QVector> runSolverExe();
- // 运行Dll求解器
- QVector> runSolverDll();
- // 验证求解器结果
- bool validateSolverResult(const QVector>& result) const;
-
- // ==================== 数据处理方法 ====================
- // 插值数据
- QVector interpolateData(const QVector& source,
- const QVector& targetX) const;
- // 计算双对数曲线误差
- double calculateLogLogCurveError(const QVector>& target,
- const QVector>& result) const;
- // 计算曲线误差
- double calculateCurveError(const QVector& curve1,
- const QVector& curve2) const;
-
- // ==================== 工具方法 ====================
- // 生成0-1随机数
- double random01() const;
- // 高斯随机数
- double gaussianRandom(double mean, double stddev) const;
- // 获取启用参数数量
- int getEnabledParameterCount() const;
- // 保存优化结果
- void saveOptimizationResult();
-
-
-private:
- // ==================== 常量定义 ====================
- static const double MIN_FITNESS_IMPROVEMENT;
- static const double MUTATION_STRENGTH;
- static const int CONVERGENCE_CHECK_INTERVAL;
- static const int MAX_STAGNATION_GENERATIONS;
-
- // ==================== 核心状态变量 ====================
- bool m_isRunning; // 是否正在运行
- bool m_shouldStop; // 是否应该停止
- bool m_isPaused; // 是否暂停
- int m_currentGeneration; // 当前代数
-
- // 适应度统计
- double m_bestFitness; // 最优适应度
- double m_worstFitness; // 最差适应度
- double m_averageFitness; // 平均适应度
- double m_previousBestFitness; // 上一代最优适应度
-
- // ==================== GA算法参数 ====================
- int m_populationSize; // 种群大小
- int m_maxGenerations; // 最大代数
- double m_targetError; // 目标误差
- double m_crossoverRate; // 交叉概率
- double m_mutationRate; // 变异概率
- double m_elitismRate; // 精英保留比例
- int m_tournamentSize; // 锦标赛选择大小
- bool m_useUniformCrossover; // 是否使用均匀交叉
-
- // ==================== 种群和个体 ====================
- QVector m_population; // 当前种群
- QVector m_eliteIndividuals; // 精英个体
- GAIndividual m_bestIndividual; // 全局最优个体
-
- // ==================== 评估统计 ====================
- int m_totalEvaluations; // 总评估次数
- int m_successfulEvaluations; // 成功评估次数
- int m_evaluationInProgress; // 正在进行的评估计数
- int m_consecutiveFailures; // 连续失败次数
-
- // ==================== 精英保护相关 ====================
- QVector m_initialValues; // 用户初始参数值
- QVector m_userInitialSolution; // 用户初始解
- double m_userInitialFitness; // 用户初始适应度
- int m_consecutiveFailedGenerations; // 连续失败代数
- int m_maxConsecutiveFailures; // 最大允许连续失败数
- bool m_hasValidUserSolution; // 是否有有效的用户解
- double m_improvementThreshold; // 改进阈值
-
- // ==================== 收敛判断相关 ====================
- double m_diversityThreshold; // 多样性阈值
- double m_convergenceVarianceThreshold; // 收敛方差阈值
- int m_trueConvergenceWindow; // 真收敛判断窗口
- int m_localOptimumWindow; // 局部最优判断窗口
- double m_nearTargetFactor; // 接近目标的因子
- double m_farTargetFactor; // 远离目标的因子
-
- // ==================== 历史记录 ====================
- QVector m_convergenceHistory; // 收敛历史
- QVector m_diversityHistory; // 多样性历史
-
- // ==================== 参数配置 ====================
- QVector m_parameterSelected; // 参数选择状态
- QVector m_parameterLower; // 参数下界
- QVector m_parameterUpper; // 参数上界
- QVector m_enabledParamIndices; // 启用参数索引
-
- // ==================== 目标数据 ====================
- QVector> m_targetLogLogData; // 目标双对数数据
-
- // ==================== 其他 ====================
- QString m_lastError; // 最后错误信息
- QTimer* m_progressTimer; // 进度更新定时器
- // DLL求解器需要的临时目录
- QString m_tempDirectory;
-
- QString m_targetWellName;// 目标井名称
-};
-
-#endif // NMCALCULATIONAUTOFITGA_H
\ No newline at end of file
diff --git a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h
index e59db09..9731259 100644
--- a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h
+++ b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include "nmCalculation_global.h"
@@ -19,6 +20,69 @@ class nmDataWellBase;
class QTimer;
class QProcess;
+// 双对数曲线误差分解。该结构同时保存用于候选排序的主目标,以及用于判断
+// 曲线上下、左右和形状偏差的诊断量。total 是唯一的接受和排序依据,诊断量
+// 只参与信赖域选参,不能再次叠加到 total,否则会重复计算同一批曲线残差。
+struct AutoFitObjectiveBreakdown {
+ // valid 表示本次曲线评价完整有效;无效评价统一保留 total=1e10。
+ // pressureLoss 和 derivativeLoss 均在 log(value) 空间按固定网格计算。
+ bool valid;
+ double total;
+ double pressureLoss;
+ double derivativeLoss;
+ // 固定目标网格上的普通对数残差。非代理搜索使用它建立完整 Jacobian,
+ // 向量平方和与 total 的平方一致。
+ QVector residualVector;
+
+ // 上下偏差使用压力和导数残差共享的算术平均中心。
+ // verticalCommonBias 为正表示模拟曲线整体偏高,为负表示整体偏低;
+ // verticalReliable=false 时仍保留数值,但不能据此确定参数调整方向。
+ double verticalCommonBias;
+ double verticalLoss;
+ bool verticalReliable;
+
+ // 水平偏差在 log(time) 坐标中计算。physicalShift 为正表示模拟曲线相对
+ // 目标偏右,即相同曲线特征在模拟结果中出现得更晚。
+ double horizontalPhysicalShift;
+ double horizontalLoss;
+ bool horizontalReliable;
+ // true 表示当前曲线无法可靠区分上下和左右误差;此时禁止使用两类有符号
+ // 诊断量选参,但去除公共中心后的 shapeLoss 仍可用于局部选参。
+ bool registrationAmbiguous;
+
+ // 去除公共均值中心和可信左右偏差后剩余的整体形状误差;verticalReliable
+ // 只控制能否把公共中心解释为上下参数方向,不改变 shape 的中心化公式。
+ double shapeLoss;
+
+ // 兼容现有 trace 列。当前非代理搜索不再单独识别或调度晚期分量。
+ double lateDerivativeSlopeBias;
+ double lateDerivativeTrendLoss;
+ bool lateDerivativeTrendReliable;
+
+ // 模拟曲线对目标固定网格的有效覆盖率,取覆盖点比例与连续 log-time
+ // 跨度比例中的较小值。低于损失函数门槛时本次评价直接无效。
+ double coverage;
+
+ AutoFitObjectiveBreakdown()
+ : valid(false)
+ , total(1.0e10)
+ , pressureLoss(std::numeric_limits::quiet_NaN())
+ , derivativeLoss(std::numeric_limits::quiet_NaN())
+ , verticalCommonBias(std::numeric_limits::quiet_NaN())
+ , verticalLoss(std::numeric_limits::quiet_NaN())
+ , verticalReliable(false)
+ , horizontalPhysicalShift(std::numeric_limits::quiet_NaN())
+ , horizontalLoss(std::numeric_limits::quiet_NaN())
+ , horizontalReliable(false)
+ , registrationAmbiguous(false)
+ , shapeLoss(std::numeric_limits::quiet_NaN())
+ , lateDerivativeSlopeBias(std::numeric_limits::quiet_NaN())
+ , lateDerivativeTrendLoss(std::numeric_limits::quiet_NaN())
+ , lateDerivativeTrendReliable(false)
+ , coverage(std::numeric_limits::quiet_NaN())
+ {}
+};
+
// PSO粒子结构
// 这里的 position / velocity / bestPosition 只保存“用户勾选参与拟合的参数”,
// 不是完整的 11 个储层/井筒参数。完整参数向量会在写 trace 或调用代理模型时
@@ -33,6 +97,8 @@ struct AutoFitParticle {
QVector velocity; // 速度
QVector bestPosition; // 真实求解器确认的个体最优位置
QVector guideBestPosition; // 仅用于速度更新的引导位置;不会参与真实 gbest/最终结果
+ AutoFitObjectiveBreakdown currentObjectiveBreakdown; // 当前真实评价对应的误差分解
+ AutoFitObjectiveBreakdown bestObjectiveBreakdown; // pbest 对应的误差分解
double fitness; // 当前适应度
double bestFitness; // 真实求解器确认的个体最优适应度
double guideBestObjective; // guideBestPosition 对应的真实或代理目标值
@@ -96,6 +162,7 @@ public:
void stopFitting();
QVector getBestSolution() const;
double getBestFitness() const;
+ AutoFitObjectiveBreakdown getLastObjectiveBreakdown() const;
QString getLastError() const;
bool isRunning() const;
int getCurrentIteration() const;
@@ -137,20 +204,26 @@ private:
void loadOptimizationConfig();
void loadParameterBounds();
- // ===== PSO核心算法 =====
+ // ===== 自动拟合核心算法 =====
//
- // 主流程:
- // 1. extractUserInitialValues(): 从当前项目数据中取用户已有初始解;
- // 2. initializeSwarm(): 根据初始解和上下界生成粒子群;
- // 3. updateParticle(): 对单个粒子跑真实求解器并计算误差;
- // 4. updateGlobalBest(): 只用真实求解器误差更新全局最优;
- // 5. updateVelocityAndPosition(): 按 PSO 公式推进下一代粒子。
+ // 代理开启时保留原 PSO 筛选流程;代理关闭时使用真实求解器驱动的
+ // 诊断灵敏度信赖域搜索,不依赖 pbest/gbest 速度公式。
void extractUserInitialValues();
void initializeSwarm();
void updateVelocityAndPosition();
double evaluateFitness(const QVector& parameters);
void updateGlobalBest();
void updateParticle(int particleIndex);
+ // 非代理拟合入口:建立有限差分灵敏度,按诊断分量选择参数,再用有界
+ // LM/信赖域产生候选;所有候选最终都由真实求解器总误差决定是否接受。
+ StopReasonPSO runTrustRegionFitting();
+ // 对一个信赖域候选执行完整真实评价,并一次性返回误差、诊断量、曲线和耗时。
+ // 返回 false 表示求解失败、损失无效或用户已请求停止。
+ bool evaluateTrustRegionPoint(const QVector& parameters,
+ double* fitness,
+ AutoFitObjectiveBreakdown* breakdown,
+ QVector >* curve,
+ int* elapsedMs);
// ===== 参数应用方法 =====
//
@@ -166,6 +239,7 @@ private:
// ===== 求解器相关 =====
QVector > runSolver();
QVector> runSolverDll();
+ bool runFinalFullSolver();
QVector> runSolverExe();
// ===== 数据处理 =====
@@ -220,7 +294,8 @@ private:
double surrogateObjective,
const QString& screeningDecision,
const QVector& pbestPosition,
- double pbestObjective);
+ double pbestObjective,
+ const AutoFitObjectiveBreakdown* objectiveBreakdown = nullptr);
void writeIterationTraceRows();
QVector buildTraceParameterVector(const QVector& selectedParameters) const;
void resetRunSummary();
@@ -276,30 +351,34 @@ private:
bool m_isRunning; // 当前是否有一次自动拟合正在运行。
bool m_shouldStop; // 用户停止标志;主循环和求解器等待循环会定期检查它。
bool m_isPaused; // 预留暂停标志;主循环中有暂停等待逻辑。
- int m_currentIteration; // 当前 PSO 迭代序号,从 0 开始。
+ int m_currentIteration; // 当前自动拟合迭代序号,从 0 开始。
QString m_lastError; // 最近一次失败原因,供 UI 展示或日志排查。
- // ===== PSO数据 =====
+ // ===== 优化状态数据 =====
QVector m_initialValues; // 当前模型中提取的用户初始值,顺序与 m_enabledParamIndices 一致。
QVector m_swarm; // 粒子群,每个粒子只保存启用参数维度。
- QVector m_globalBestPosition; // 全局最优参数,仍是启用参数向量。
+ QVector m_globalBestPosition; // 真实求解器确认的当前最优参数。
double m_globalBestFitness; // 全局最优真实误差,越小越好。
double m_previousBestFitness; // 上一轮全局最优误差,用于自适应参数更新。
+ AutoFitObjectiveBreakdown m_globalBestObjectiveBreakdown; // 真实 gbest 对应的误差分解。
QVector > m_lastEvaluatedLogLogData; // 最近一次真实求解得到的 result log-log 曲线。
QVector > m_globalBestLogLogData; // 当前全局最优对应的 result log-log 曲线。
+ mutable AutoFitObjectiveBreakdown m_lastObjectiveBreakdown; // 最近一次损失评价的误差分解。
QVector > m_userInitialLogLogData; // 用户初始解对应的 result log-log 曲线,用于精英保护。
+ AutoFitObjectiveBreakdown m_userInitialObjectiveBreakdown; // 用户初始解对应的误差分解。
// ===== 优化配置 =====
//
// 参数索引约定:
// 0 k 渗透率;1 skin 表皮系数;2 wellboreC 井筒储集;
// 3 phi 孔隙度;4 h 储层厚度;5 Ct 综合压缩系数;
- // 6 Cf 岩石压缩系数;7 Swi 初始含水饱和度。
+ // 6 Cf 岩石压缩系数;7 Swi 初始含水饱和度;
+ // 8 Dfc 裂缝导流能力;9 fractureHalfLength 裂缝半长。
// m_enabledParamIndices 保存被用户勾选的参数索引,粒子的 position 维度与它一致。
- QVector m_parameterSelected; // 完整 8 个参数是否被用户勾选参与拟合。
- QVector m_parameterLower; // 完整 8 个参数的搜索下界。
- QVector m_parameterUpper; // 完整 8 个参数的搜索上界。
- QVector m_enabledParamIndices; // 被勾选参数在完整 8 维体系中的索引。
+ QVector m_parameterSelected; // 完整 10 个参数是否被用户勾选参与拟合。
+ QVector m_parameterLower; // 完整 10 个参数的搜索下界。
+ QVector m_parameterUpper; // 完整 10 个参数的搜索上界。
+ QVector m_enabledParamIndices; // 被勾选参数在完整 10 维体系中的索引。
QVector > m_targetLogLogData; // 目标井 history log-log 曲线:time/pressure/derivative。
QString m_targetWellName; // 目标井名称;读写井参数和读取模拟曲线都依赖它。
@@ -312,7 +391,7 @@ private:
double m_socialParam; // 群体学习因子,控制粒子靠近全局 gbest 的程度。
// ===== 统计信息 =====
- int m_totalEvaluations; // 已调用真实求解器评价的粒子总数。
+ int m_totalEvaluations; // 真实求解器评价总次数,包含粒子评价和方向试算。
int m_successfulEvaluations; // 真实求解器成功且误差有效的评价次数。
QVector m_convergenceHistory; // 每代全局最优误差历史,用于收敛判断。
@@ -328,7 +407,7 @@ private:
// ===== 精英保护 =====
QVector m_userInitialSolution; // 用户初始解参数,若最终改进不足会恢复它。
double m_userInitialFitness; // 用户初始解真实误差。
- double m_improvementThreshold; // 最终结果相对初始解至少需要达到的改进阈值。
+ double m_improvementThreshold; // 仅用于日志区分显著改进和微小改进。
bool m_hasValidUserSolution; // 初始解是否成功跑过真实求解器。
int m_consecutiveFailedIterations; // 连续失败迭代次数
@@ -357,8 +436,8 @@ private:
// 这些字段只描述代理筛选和运行复盘,不参与 PSO 数学更新。
bool m_traceEnabled; // 是否写出 trace CSV/meta 文件。
QString m_traceRunId; // 本次运行 ID,作为 trace/candidate/score 文件名的一部分。
- QString m_traceFilePath; // pso_baseline_trace_.csv 完整路径。
- QString m_traceMetaFilePath; // pso_baseline_trace_.meta.json 完整路径。
+ QString m_traceFilePath; // 本次自动拟合 trace CSV 的完整路径。
+ QString m_traceMetaFilePath; // 与 trace 匹配的 meta JSON 完整路径。
QFile m_traceFile; // trace CSV 文件句柄。
bool m_surrogateScreeningEnabled; // 用户配置中的 PSO acceleration 开关。
unsigned int m_psoRandomSeed; // PSO 随机种子,也用于可复现 random audit。
diff --git a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h
index 1de9f71..3efc33f 100644
--- a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h
+++ b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h
@@ -2,6 +2,8 @@
#define NMCALCULATIONDLLPEBISOLVERTASK_H
#include
+#include
+#include
#include
#include
#include
@@ -22,6 +24,13 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
// 返回 false 时调用方会丢弃本次结果, 防止复用上一粒子留下的旧曲线.
bool wasSuccessful() const;
+ // 自动拟合粒子评价只提取目标井曲线,不写回共享数据和网格压力场。
+ // 井名为空时保持原有完整结果保存模式。
+ void setAutoFitTargetWell(const QString& wellName);
+ QVector > getAutoFitResultPressure() const;
+ QVector > getAutoFitResultLogLog() const;
+ QVector > getAutoFitResultSemiLog() const;
+
private:
bool execute();
@@ -37,6 +46,10 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
QString m_sPostprocessingDir;
// run() 在线程内保存 execute() 结果, 等待线程结束的调用方只读取该状态.
bool m_lastRunSucceeded;
+ QString m_autoFitTargetWellName;
+ QVector > m_autoFitResultPressure;
+ QVector > m_autoFitResultLogLog;
+ QVector > m_autoFitResultSemiLog;
private slots:
//void slotTaskUpdateProgress();
diff --git a/Include/nmNum/nmData/nmDataAutomaticFitting.h b/Include/nmNum/nmData/nmDataAutomaticFitting.h
index b2da223..98829eb 100644
--- a/Include/nmNum/nmData/nmDataAutomaticFitting.h
+++ b/Include/nmNum/nmData/nmDataAutomaticFitting.h
@@ -75,6 +75,20 @@ public:
nmDataAttribute& getSwiMin();
void setSwiMin(const nmDataAttribute& swiMin);
+ // Getter and Setter for fractureConductivityMax
+ nmDataAttribute& getFractureConductivityMax();
+ void setFractureConductivityMax(const nmDataAttribute& fractureConductivityMax);
+ // Getter and Setter for fractureConductivityMin
+ nmDataAttribute& getFractureConductivityMin();
+ void setFractureConductivityMin(const nmDataAttribute& fractureConductivityMin);
+
+ // Getter and Setter for fractureHalfLengthMax
+ nmDataAttribute& getFractureHalfLengthMax();
+ void setFractureHalfLengthMax(const nmDataAttribute& fractureHalfLengthMax);
+ // Getter and Setter for fractureHalfLengthMin
+ nmDataAttribute& getFractureHalfLengthMin();
+ void setFractureHalfLengthMin(const nmDataAttribute& fractureHalfLengthMin);
+
// Getter and Setter for iteration count
nmDataAttribute& getIterationCount();
void setIterationCount(const nmDataAttribute& iterationCount);
@@ -114,6 +128,12 @@ public:
bool getSwiSelected() const;
void setSwiSelected(bool selected);
+ bool getFractureConductivitySelected() const;
+ void setFractureConductivitySelected(bool selected);
+
+ bool getFractureHalfLengthSelected() const;
+ void setFractureHalfLengthSelected(bool selected);
+
private:
// 参数最大值
nmDataAttribute m_permeabilityMax;
@@ -124,6 +144,8 @@ private:
nmDataAttribute m_ctMax;
nmDataAttribute m_cfMax;
nmDataAttribute m_swiMax;
+ nmDataAttribute m_fractureConductivityMax;
+ nmDataAttribute m_fractureHalfLengthMax;
// 参数最小值
nmDataAttribute m_permeabilityMin;
@@ -134,6 +156,8 @@ private:
nmDataAttribute m_ctMin;
nmDataAttribute m_cfMin;
nmDataAttribute m_swiMin;
+ nmDataAttribute m_fractureConductivityMin;
+ nmDataAttribute m_fractureHalfLengthMin;
// 迭代参数
nmDataAttribute m_iterationCount; // 迭代步数
@@ -150,6 +174,8 @@ private:
bool m_ctSelected; // 是否选择综合压缩系数进行拟合
bool m_cfSelected; // 是否选择岩石压缩系数进行拟合
bool m_swiSelected; // 是否选择初始含水饱和度进行拟合
+ bool m_fractureConductivitySelected; // 是否选择裂缝导流能力进行拟合
+ bool m_fractureHalfLengthSelected; // 是否选择裂缝半长进行拟合
};
#endif // NMDATAAUTOMATICFITTING_H
diff --git a/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h b/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h
index a245aac..a8c4f76 100644
--- a/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h
+++ b/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h
@@ -21,17 +21,10 @@
#include "nmDataWellBase.h"
#include "nmDataAutomaticFitting.h"
#include "nmCalculationAutoFitPSO.h"
-#include "nmCalculationAutoFitGA.h"
#include "nmWxAutomaticfittingStart.h"
#include "nmSubWxs_global.h"
-// 算法类型枚举
-enum OptimizationAlgorithm {
- ALGORITHM_PSO = 0,
- ALGORITHM_GA = 1
-};
-
class NM_SUB_WXS_EXPORT nmWxAutomaticFitting : public iDlgBase
{
Q_OBJECT
@@ -46,7 +39,7 @@ public:
void onAccept();
void onReject();
void onWellSelected(int index);
- void onAlgorithmChanged(int index);
+ void onParameterTableItemChanged(QTableWidgetItem* item);
// 自动拟合相关槽函数
void runAutoFitting();
@@ -63,6 +56,12 @@ private:
void setParameterRowVisible(QTableWidget* table, int row, bool visible);
void renumberVisibleParameterRows(QTableWidget* table);
void updateParameterVisibility(QTableWidget* table, NM_SOLVER_MODEL_TYPE eType);
+ void initializeSuggestedParameterRanges();
+ void updateRangeForParameter(int parameterIndex, double centerValue);
+ void setParameterRange(int parameterIndex, double minValue, double maxValue);
+ bool getPhysicalParameterRange(int parameterIndex, double& minValue, double& maxValue);
+ void normalizeSavedParameterRanges();
+ bool validateParameterTable(QString& errorMessage, int parameterIndex = -1);
void startAutoFitting(const QVector>& targetData, const QStringList& selectedParams, const QString& targetWellName);
void cleanupFitting();
@@ -92,6 +91,8 @@ private:
QCheckBox* m_ctCheckBox; // 综合压缩系数
QCheckBox* m_cfCheckBox; // 岩石压缩系数
QCheckBox* m_swiCheckBox; // 初始含水饱和度
+ QCheckBox* m_dfcCheckBox; // 裂缝导流能力
+ QCheckBox* m_fractureHalfLengthCheckBox; // 裂缝半长
// 按钮
QPushButton* m_reverseBtn;
@@ -108,10 +109,10 @@ private:
// 自动拟合相关成员
nmCalculationAutoFitPSO* m_autoFitterPSO;
- nmCalculationAutoFitGA* m_autoFitterGA;
QProgressDialog* m_progressDialog;
QTimer* m_progressTimer;
- OptimizationAlgorithm m_selectedAlgorithm; // 选中的算法类型
+ bool m_autoParameterRanges;
+ bool m_updatingParameterRanges;
// 拟合开始界面
nmWxAutomaticfittingStart* m_progressMonitor;
diff --git a/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h b/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h
index eb37eeb..3e9067e 100644
--- a/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h
+++ b/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h
@@ -22,12 +22,10 @@
#include
#include
-#include "nmCalculationAutoFitGA.h"
#include "nmCalculationAutoFitPSO.h"
// 前向声明
class nmCalculationAutoFitPSO;
-class nmCalculationAutoFitGA;
class QPainter;
class QColor;
class QPaintEvent;
@@ -68,12 +66,6 @@ private:
bool m_pseudoPressureMode;
};
-// 算法类型枚举
-enum FittingAlgorithmType {
- FITTING_ALGORITHM_PSO = 0,
- FITTING_ALGORITHM_GA = 1
-};
-
class nmWxAutomaticfittingStart : public iDlgBase
{
Q_OBJECT
@@ -85,8 +77,6 @@ public:
// PSO算法接口
void setAutoFitter(nmCalculationAutoFitPSO* autoFitter);
- // GA算法接口
- void setAutoFitterGA(nmCalculationAutoFitGA* autoFitter);
// 通用设置接口
void setFittingParameters(int maxIterations, double targetError, const QString& wellName);
@@ -161,8 +151,6 @@ private:
// 算法实例
nmCalculationAutoFitPSO* m_autoFitterPSO;
- nmCalculationAutoFitGA* m_autoFitterGA;
- FittingAlgorithmType m_algorithmType;
// 拟合参数
int m_maxIterations;
diff --git a/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h b/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h
index 720d0bb..8a9728b 100644
--- a/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h
+++ b/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h
@@ -5,6 +5,7 @@
class nmGridRowUtils;
class nmDataAnalyzeManager;
+class QTimer;
/// @brief PEBI求解器专用的参数输入/编辑属性对话框窗体
/// @note 与 nmWxParaProperty 结构一致,区别在于内部使用 nmGridRowUtils(NM子类)
@@ -76,6 +77,8 @@ public slots:
void slotWellRemoved(QString wellCode, QString wellName);
/// @brief 几何对象新增、删除或改名后重建参数面板
void slotGeometryListChanged();
+ /// @brief 合并同一轮事件中的多次井列表变化,只重建一次参数面板
+ void slotDeferredWellListRebuild();
protected:
@@ -93,6 +96,13 @@ protected:
// 数据管理器引用(用于全量重建面板时获取井和几何对象列表)
nmDataAnalyzeManager* m_pDataManager;
+ // 批量增删井时合并参数面板刷新
+ QTimer* m_pWellListRebuildTimer;
+
+private:
+
+ void scheduleWellListRebuild();
+
signals:
/// @brief 参数值变更信号(由 slotParaCtrlValueChanged 转发)
diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitGA.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitGA.cpp
deleted file mode 100644
index 45eda30..0000000
--- a/Src/nmNum/nmCalculation/nmCalculationAutoFitGA.cpp
+++ /dev/null
@@ -1,2911 +0,0 @@
-#include "nmCalculationAutoFitGA.h"
-//#include "nmCalculationExeSolverTask.h"
-#include "nmCalculationDllPebiSolverTask.h"
-#include "nmDataAnalyzeManager.h"
-#include "nmDataWellBase.h"
-#include "nmDataVerticalWell.h"
-#include "nmDataVerticalFracturedWell.h"
-#include "nmDataHorizontalFracturedWell.h"
-#include "nmDataReservoir.h"
-#include "nmDataAutomaticFitting.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef Q_OS_WIN
-#include
-#include
-#define DEBUG_OUT(msg) OutputDebugStringA(QString("[AutoFit] %1\n").arg(msg).toLocal8Bit().data())
-#endif
-
-// 常量定义
-const double nmCalculationAutoFitGA::MIN_FITNESS_IMPROVEMENT = 1e-8;
-const double nmCalculationAutoFitGA::MUTATION_STRENGTH = 0.1;
-const int nmCalculationAutoFitGA::CONVERGENCE_CHECK_INTERVAL = 10;
-const int nmCalculationAutoFitGA::MAX_STAGNATION_GENERATIONS = 20;
-
-// 无穷大和NaN检查
-static inline bool isFiniteNumber(double value)
-{
-#ifdef Q_OS_WIN
- return _finite(value) != 0 && !_isnan(value);
-#else
- return std::isfinite(value);
-#endif
-}
-
-// sleep函数
-static inline void msleep(int ms)
-{
-#ifdef Q_OS_WIN
- Sleep(ms);
-#endif
-}
-
-// 构造函数
-nmCalculationAutoFitGA::nmCalculationAutoFitGA(QObject* parent)
- : QObject(parent)
- , m_isRunning(false)
- , m_shouldStop(false)
- , m_isPaused(false)
- , m_currentGeneration(0)
- , m_bestFitness(1e10)
- , m_worstFitness(-1e10)
- , m_averageFitness(1e10)
- , m_previousBestFitness(1e10)
- , m_populationSize(40)
- , m_maxGenerations(100)
- , m_targetError(0.001)
- , m_crossoverRate(0.8)
- , m_mutationRate(0.1)
- , m_elitismRate(0.1)
- , m_tournamentSize(3)
- , m_useUniformCrossover(true)
- , m_totalEvaluations(0)
- , m_successfulEvaluations(0)
- , m_evaluationInProgress(0)
- , m_consecutiveFailures(0)
- , m_progressTimer(0)
- , m_userInitialFitness(1e10)
- , m_consecutiveFailedGenerations(0)
- , m_maxConsecutiveFailures(3)
- , m_hasValidUserSolution(false)
- , m_improvementThreshold(0.05)
- , m_diversityThreshold(0.05)
- , m_convergenceVarianceThreshold(1e-8)
- , m_trueConvergenceWindow(15)
- , m_localOptimumWindow(8)
- , m_nearTargetFactor(2.0)
- , m_farTargetFactor(10.0)
- , m_targetWellName("")
-{
- DEBUG_OUT(QString("GA Constructor: this=0x%1").arg((quintptr)this, 0, 16));
-
- // 初始化随机数种子
- qsrand(QTime::currentTime().msec());
-
- // 初始化临时目录用于DLL求解器
- initializeTemporaryDirectory();
-
- // 初始化最优个体
- m_bestIndividual.fitness = 1e10;
- m_bestIndividual.isEvaluated = false;
-
- // 创建进度更新定时器
- m_progressTimer = new QTimer(this);
- connect(m_progressTimer, SIGNAL(timeout()), this, SLOT(updateProgress()));
-
- DEBUG_OUT("AutoFit GA calculator initialized (data-driven mode)");
- DEBUG_OUT("GA Constructor completed");
-}
-
-// 析构函数
-nmCalculationAutoFitGA::~nmCalculationAutoFitGA()
-{
- DEBUG_OUT(QString("GA Destructor: this=0x%1").arg((quintptr)this, 0, 16));
-
- // 首先停止算法
- if(m_isRunning) {
- m_shouldStop = true; // 立即设置停止标志
-
- // 等待当前操作完成,增加超时时间
- int waitCount = 0;
- while(m_isRunning && waitCount < 100) {
- QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 50);
- msleep(50);
- waitCount++;
- }
-
- // 如果仍在运行则强制停止
- if(m_isRunning) {
- DEBUG_OUT("Force stopping GA - timeout reached");
- m_isRunning = false;
- }
- }
-
- // 清理临时目录
- cleanupTemporaryDirectory();
-
- // 断开所有信号连接,防止回调已销毁的对象
- disconnect(this, nullptr, nullptr, nullptr);
-
- DEBUG_OUT("GA Destructor completed");
-}
-
-// ==================== 目录处理方法 ====================
-
-void nmCalculationAutoFitGA::initializeTemporaryDirectory()
-{
- QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz");
- QString processId = QString::number(QCoreApplication::applicationPid());
-
- m_tempDirectory = QApplication::applicationDirPath() +
- "/autofit_temp_" + processId + "_" + timestamp;
-
- // 确保目录不存在
- int counter = 0;
- QString originalPath = m_tempDirectory;
-
- while(QDir(m_tempDirectory).exists() && counter < 100) {
- m_tempDirectory = originalPath + "_" + QString::number(counter);
- counter++;
- }
-
- if(QDir().mkpath(m_tempDirectory)) {
- DEBUG_OUT(QString("Initialized temp directory: %1").arg(m_tempDirectory));
- } else {
- DEBUG_OUT(QString("Warning: Failed to create temp directory: %1").arg(m_tempDirectory));
- m_tempDirectory = QApplication::applicationDirPath();
- }
-}
-
-void nmCalculationAutoFitGA::cleanupTemporaryDirectory()
-{
- if(QDir(m_tempDirectory).exists()) {
- if(removeDirectoryRecursively(m_tempDirectory)) {
- DEBUG_OUT("Temp directory cleaned up successfully");
- } else {
- DEBUG_OUT("Warning: Failed to clean up temp directory completely");
- }
- }
-}
-
-bool nmCalculationAutoFitGA::removeDirectoryRecursively(const QString& path)
-{
- QDir dir(path);
-
- if(!dir.exists()) {
- return true;
- }
-
- // 递归删除子目录和文件
- QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
- bool allRemoved = true;
-
- for(int i = 0; i < entries.size(); ++i) {
- const QFileInfo& entry = entries[i];
-
- if(entry.isDir()) {
- if(!removeDirectoryRecursively(entry.absoluteFilePath())) {
- allRemoved = false;
- }
- } else {
- QFile file(entry.absoluteFilePath());
-
- // 处理只读文件
- if(!file.permissions().testFlag(QFile::WriteUser)) {
- file.setPermissions(file.permissions() | QFile::WriteUser);
- }
-
- if(!file.remove()) {
- DEBUG_OUT(QString("Failed to remove file: %1").arg(entry.absoluteFilePath()));
- allRemoved = false;
- }
- }
- }
-
- // 删除目录本身
- if(allRemoved) {
- return dir.rmdir(path);
- }
-
- return false;
-}
-
-// ==================== 公共接口方法 ====================
-
-void nmCalculationAutoFitGA::setTargetLogLogData(const QVector>& targetData)
-{
- m_targetLogLogData = targetData;
- DEBUG_OUT(QString("Target LogLog data set: %1 arrays").arg(targetData.size()));
-
- if(targetData.size() >= 3) {
- DEBUG_OUT(QString("LogLog data points: X=%1, Y1=%2, Y2=%3")
- .arg(targetData[0].size())
- .arg(targetData[1].size())
- .arg(targetData[2].size()));
- }
-}
-
-bool nmCalculationAutoFitGA::startAutoFitting()
-{
- if(m_isRunning) {
- m_lastError = "GA fitting is already running";
- return false;
- }
-
- DEBUG_OUT("=== GA AUTO FITTING START ===");
-
- // 发送初始化日志
- emit logMessageGenerated(tr("=== GA Automatic Fitting Started ==="));
- emit logMessageGenerated(tr("Algorithm: Genetic Algorithm"));
-
- try {
- // 从数据管理器加载所有配置
- if(!loadAllConfigFromDataManager()) {
- emit logMessageGenerated(tr("ERROR: Failed to load configuration from data manager"));
- return false;
- }
-
- int enabledParams = getEnabledParameterCount();
- emit logMessageGenerated(tr("Enabled parameters count: %1").arg(enabledParams));
-
- if(enabledParams == 0) {
- m_lastError = "No parameters enabled for optimization";
- emit logMessageGenerated(tr("ERROR: No parameters enabled for optimization"));
- return false;
- }
-
- if(m_targetLogLogData.isEmpty() || m_targetLogLogData.size() < 3) {
- m_lastError = "Target LogLog data is empty or insufficient";
- emit logMessageGenerated(tr("ERROR: Target LogLog data is empty or insufficient"));
- return false;
- }
-
- // 检查数据一致性
- if(m_targetLogLogData[0].size() != m_targetLogLogData[1].size() ||
- m_targetLogLogData[0].size() != m_targetLogLogData[2].size()) {
- m_lastError = "Target LogLog data arrays have inconsistent sizes";
- emit logMessageGenerated(tr("ERROR: Target LogLog data arrays have inconsistent sizes"));
- return false;
- }
-
- emit logMessageGenerated(tr("Target data validation passed (%1 data points)").arg(m_targetLogLogData[0].size()));
-
- // 使用保存的初始值进行精英保护
- QVector savedInitialValues = m_initialValues;
-
- // 重置状态
- resetOptimizer();
- m_isRunning = true;
- m_shouldStop = false;
- m_isPaused = false;
- m_currentGeneration = 0;
- m_consecutiveFailures = 0;
- m_consecutiveFailedGenerations = 0;
-
- // 精英保护:评估用户初始解
- if(!savedInitialValues.isEmpty()) {
- m_userInitialSolution = savedInitialValues;
- emit logMessageGenerated(tr("=== Evaluating Initial Solution (Elite Protection) ==="));
-
- // 输出初始参数值
- QString paramStr = tr("Initial parameters: ");
- for(int i = 0; i < m_userInitialSolution.size(); ++i) {
- paramStr += QString("[%1]=%2 ").arg(i).arg(m_userInitialSolution[i], 0, 'f', 6);
- }
- emit logMessageGenerated(paramStr);
-
- try {
- emit logMessageGenerated(tr("Starting initial solution evaluation..."));
- DEBUG_OUT(QString("Before evaluateFitness: m_userInitialSolution size = %1").arg(m_userInitialSolution.size()));
-
- if(m_userInitialSolution.isEmpty()) {
- emit logMessageGenerated(tr("ERROR: m_userInitialSolution is empty!"));
- return false;
- }
-
- for(int i = 0; i < m_userInitialSolution.size(); ++i) {
- emit logMessageGenerated(tr("Initial param[%1] = %2").arg(i).arg(m_userInitialSolution[i], 0, 'f', 6));
- }
-
- m_userInitialFitness = evaluateGenes(m_userInitialSolution);
-
- emit logMessageGenerated(tr("evaluateGenes returned: %1").arg(m_userInitialFitness, 0, 'e', 10));
-
- if(m_userInitialFitness < 1e9) {
- emit logMessageGenerated(tr("Taking SUCCESS branch (fitness < 1e9)"));
- m_hasValidUserSolution = true;
- m_bestFitness = m_userInitialFitness;
- m_bestIndividual.genes = m_userInitialSolution;
- m_bestIndividual.fitness = m_userInitialFitness;
- m_bestIndividual.isEvaluated = true;
-
- emit logMessageGenerated(tr("Initial solution evaluation successful"));
- emit logMessageGenerated(tr("Initial fitness (error): %1").arg(m_userInitialFitness, 0, 'e', 4));
- emit logMessageGenerated(tr("Elite protection activated - initial solution will be preserved if no significant improvement found"));
- } else {
- emit logMessageGenerated(tr("Taking FAILURE branch (fitness >= 1e9)"));
- m_hasValidUserSolution = false;
- emit logMessageGenerated(tr("Initial solution evaluation failed - starting with random initialization"));
- }
- } catch(...) {
- m_hasValidUserSolution = false;
- emit logMessageGenerated(tr("Exception during initial solution evaluation"));
- }
-
- // 恢复初始值供种群初始化使用
- m_initialValues = savedInitialValues;
- }
-
- // 初始化种群
- initializePopulation();
- emit logMessageGenerated(tr("Population initialized: %1 individuals, %2 dimensions").arg(m_populationSize).arg(getEnabledParameterCount()));
-
- // GA主循环
- emit logMessageGenerated(tr("=== Starting GA Main Loop ==="));
-
- for(m_currentGeneration = 0; m_currentGeneration < m_maxGenerations && !m_shouldStop; ++m_currentGeneration) {
-
- // 每次迭代都输出标题,或者只在重要迭代输出详细信息
- bool shouldOutputDetail = (m_currentGeneration % qMax(1, m_maxGenerations / 10) == 0) ||
- (m_currentGeneration < 5) ||
- (m_currentGeneration >= m_maxGenerations - 2);
-
- // 每次迭代都输出标题
- emit logMessageGenerated(tr("--- Generation %1/%2 ---").arg(m_currentGeneration + 1).arg(m_maxGenerations));
-
- // 只在特定迭代输出详细统计信息
- if(shouldOutputDetail) {
- emit logMessageGenerated(tr("Current best error: %1").arg(m_bestFitness, 0, 'e', 4));
- emit logMessageGenerated(tr("Total evaluations: %1 (successful: %2, failures: %3)")
- .arg(m_totalEvaluations).arg(m_successfulEvaluations).arg(m_totalEvaluations - m_successfulEvaluations));
- }
-
- // 检查暂停状态
- while(m_isPaused && !m_shouldStop) {
- QApplication::processEvents();
- //msleep(100);
- }
-
- if(m_shouldStop) {
- emit logMessageGenerated(tr("Optimization stopped by user request"));
- break;
- }
-
- // 1. 评估种群
- evaluatePopulation();
-
- if(m_shouldStop) break;
-
- // 2. 更新统计信息
- updatePopulationStatistics();
-
- // 3. 发射进度信号
- emit progressUpdated(m_currentGeneration, m_bestFitness);
-
- if(shouldOutputDetail) {
- emit logMessageGenerated(tr("Generation %1 completed: best = %2, avg = %3, worst = %4")
- .arg(m_currentGeneration + 1).arg(m_bestFitness, 0, 'e', 4)
- .arg(m_averageFitness, 0, 'e', 4).arg(m_worstFitness, 0, 'e', 4));
- }
-
- // 记录收敛历史
- m_convergenceHistory.append(m_bestFitness);
-
- // 更新收敛指标
- updateConvergenceMetrics();
-
- // 智能收敛判断
- StopReasonGA stopReason = analyzeOptimizationStatus();
-
- if(stopReason == GA_TARGET_ACHIEVED) {
- emit logMessageGenerated(tr("=== TARGET ACHIEVED ==="));
- emit logMessageGenerated(tr("Target error achieved! Current error: %1 < Target: %2")
- .arg(m_bestFitness, 0, 'e', 4).arg(m_targetError, 0, 'e', 4));
- emit logMessageGenerated(tr("Optimization completed successfully after %1 generations")
- .arg(m_currentGeneration + 1));
- break;
- }
- else if(stopReason == GA_TRUE_CONVERGENCE) {
- emit logMessageGenerated(tr("=== TRUE CONVERGENCE DETECTED ==="));
- emit logMessageGenerated(tr("Algorithm has converged to a stable solution"));
- emit logMessageGenerated(tr("Final error: %1 after %2 generations")
- .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1));
- emit logMessageGenerated(tr("Solution quality: %1 (1.0 = target achieved)")
- .arg(m_targetError / qMax(1e-10, m_bestFitness), 0, 'f', 3));
- break;
- }
- else if(stopReason == GA_LOCAL_OPTIMUM) {
- emit logMessageGenerated(tr("=== LOCAL OPTIMUM DETECTED ==="));
- emit logMessageGenerated(tr("Algorithm appears to be trapped in local optimum"));
- emit logMessageGenerated(tr("Current error: %1 after %2 generations")
- .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1));
- emit logMessageGenerated(tr("Suggestion: Try restarting with different parameters or larger search space"));
- break;
- }
- else if(stopReason == GA_CONSECUTIVE_FAILURES) {
- emit logMessageGenerated(tr("=== CONSECUTIVE FAILURES ==="));
- emit logMessageGenerated(tr("Too many consecutive failed generations (%1/%2)")
- .arg(m_consecutiveFailedGenerations).arg(m_maxConsecutiveFailures));
- break;
- }
- else if(stopReason == GA_CONTINUE_OPTIMIZATION) {
- // 继续优化,每10次迭代输出一次状态
- if(m_currentGeneration > 0 && m_currentGeneration % 10 == 0) {
- double diversity = calculatePopulationDiversity();
-
- emit logMessageGenerated(tr(" Optimization status: diversity=%1")
- .arg(diversity, 0, 'f', 4));
- }
- }
-
- // 4. 创建新一代种群
- if(m_currentGeneration < m_maxGenerations - 1)
- {
- QVector newPopulation;
- newPopulation.reserve(m_populationSize);
-
- // 精英保留
- applyElitism(newPopulation);
-
- // 生成新个体直到填满种群
- while(newPopulation.size() < m_populationSize && !m_shouldStop) {
- // 选择父代
- int parent1Index = tournamentSelection();
- int parent2Index = tournamentSelection();
-
- // 确保父代不同
- while(parent1Index == parent2Index && m_population.size() > 1) {
- parent2Index = tournamentSelection();
- }
-
- GAIndividual offspring1, offspring2;
-
- // 交叉
- if(random01() < m_crossoverRate) {
- crossover(m_population[parent1Index], m_population[parent2Index],
- offspring1, offspring2);
- } else {
- offspring1 = m_population[parent1Index];
- offspring2 = m_population[parent2Index];
- }
-
- // 变异
- if(random01() < m_mutationRate) {
- mutate(offspring1);
- }
- if(random01() < m_mutationRate) {
- mutate(offspring2);
- }
-
- // 边界约束
- clampToLimits(offspring1.genes);
- clampToLimits(offspring2.genes);
-
- // 添加到新种群
- if(newPopulation.size() < m_populationSize) {
- newPopulation.append(offspring1);
- }
- if(newPopulation.size() < m_populationSize) {
- newPopulation.append(offspring2);
- }
- }
-
- // 替换种群
- m_population = newPopulation;
-
- // 自适应参数调整
- adaptiveParameterUpdate(m_currentGeneration);
- }
-
- // 输出迭代结束标记
- if(shouldOutputDetail) {
- emit logMessageGenerated(tr("Generation %1 completed - Current best: %2")
- .arg(m_currentGeneration + 1).arg(m_bestFitness, 0, 'e', 4));
- }
-
- // 强制处理事件,保持界面响应
- QApplication::processEvents();
-
- // 在世代间稍作停顿,减少系统负载
- if(m_currentGeneration % 3 == 2) {
- //msleep(200);
- }
- }
-
- // 最终结果验证和保护
- validateAndProtectFinalResult();
-
- } catch(const std::exception& e) {
- m_lastError = QString("Critical exception in GA main loop: %1").arg(e.what());
- emit logMessageGenerated(tr("CRITICAL ERROR: %1").arg(e.what()));
- cleanupTemporaryDirectory();
- m_isRunning = false;
- if(m_progressTimer) m_progressTimer->stop();
- emit fittingFinished(false, m_lastError);
- return false;
- } catch(...) {
- m_lastError = "Unknown critical exception in GA main loop";
- emit logMessageGenerated(tr("CRITICAL ERROR: Unknown exception in GA main loop"));
- cleanupTemporaryDirectory();
- m_isRunning = false;
- if(m_progressTimer) m_progressTimer->stop();
- emit fittingFinished(false, m_lastError);
- return false;
- }
-
- m_isRunning = false;
- if(m_progressTimer) m_progressTimer->stop();
-
- // 应用最终参数
- if(!m_bestIndividual.genes.isEmpty()) {
- try {
- emit logMessageGenerated(tr("Applying optimized parameters to model..."));
- applyParametersToDataManager(m_bestIndividual.genes);
- saveOptimizationResult();
-
- // 输出最终优化结果
- emit logMessageGenerated(tr("=== Optimization Results ==="));
- emit logMessageGenerated(tr("Final error: %1").arg(m_bestFitness, 0, 'e', 4));
- emit logMessageGenerated(tr("Total generations: %1").arg(m_currentGeneration + 1));
- emit logMessageGenerated(tr("Total evaluations: %1 (successful: %2)")
- .arg(m_totalEvaluations).arg(m_successfulEvaluations));
-
- // 输出最优参数值
- QString finalParams = tr("Optimized parameters: ");
- for(int i = 0; i < m_bestIndividual.genes.size(); ++i) {
- finalParams += QString("[%1]=%2 ").arg(i).arg(m_bestIndividual.genes[i], 0, 'f', 6);
- }
- emit logMessageGenerated(finalParams);
-
- emit logMessageGenerated(tr("Parameters applied successfully to data manager"));
- } catch(const std::exception& e) {
- emit logMessageGenerated(tr("ERROR: Failed to apply final parameters: %1").arg(e.what()));
- m_lastError = QString("Failed to apply final parameters: %1").arg(e.what());
- } catch(...) {
- emit logMessageGenerated(tr("ERROR: Unknown error applying final parameters"));
- m_lastError = "Failed to apply final parameters due to unknown error";
- }
- }
-
- // 判断系统确定最终结果
- bool success;
- QString message;
- StopReasonGA finalReason = analyzeOptimizationStatus();
-
- if(finalReason == GA_TARGET_ACHIEVED) {
- success = true;
- //message = QString("GA optimization completed successfully. Target achieved. Best error: %1, Generations: %2")
- // .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1);
- emit logMessageGenerated(tr("=== GA OPTIMIZATION SUCCESSFUL ==="));
- }
- else if(finalReason == GA_TRUE_CONVERGENCE) {
- success = true;
- //message = QString("GA optimization converged to stable solution. Best error: %1, Generations: %2")
- // .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1);
- emit logMessageGenerated(tr("=== GA OPTIMIZATION CONVERGED ==="));
- }
- else if(finalReason == GA_LOCAL_OPTIMUM) {
- success = true;
- //message = QString("GA optimization trapped in local optimum. Best error: %1, Generations: %2")
- // .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1);
- emit logMessageGenerated(tr("=== GA OPTIMIZATION - LOCAL OPTIMUM ==="));
- }
- else if(finalReason == GA_MAX_ITERATIONS) {
- success = true;
- //message = QString("GA optimization completed. Max generations reached. Best error: %1, Generations: %2")
- // .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1);
- emit logMessageGenerated(tr("=== GA OPTIMIZATION - MAX GENERATIONS ==="));
- }
- else if(finalReason == GA_USER_STOPPED) {
- success = true;
- //message = QString("GA optimization stopped by user. Best error: %1, Generations: %2")
- // .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1);
- emit logMessageGenerated(tr("=== GA OPTIMIZATION STOPPED BY USER ==="));
- }
- else if(finalReason == GA_CONSECUTIVE_FAILURES) {
- success = false;
- //message = QString("GA optimization failed due to consecutive failures. Best error: %1, Generations: %2")
- // .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1);
- emit logMessageGenerated(tr("=== GA OPTIMIZATION FAILED ==="));
- }
- else {
- success = false;
- //message = QString("GA optimization ended unexpectedly. Best error: %1, Generations: %2")
- // .arg(m_bestFitness, 0, 'e', 4).arg(m_currentGeneration + 1);
- emit logMessageGenerated(tr("=== GA OPTIMIZATION - UNKNOWN END ==="));
- }
-
- emit logMessageGenerated(tr("Result: %1").arg(success ? "SUCCESS" : "FAILED"));
-
- emit fittingFinished(success, message);
- cleanupTemporaryDirectory();
- return success;
-}
-
-void nmCalculationAutoFitGA::stopFitting()
-{
- if(m_isRunning) {
- DEBUG_OUT("Stop request received, setting stop flag...");
-
- // 添加停止日志
- emit logMessageGenerated(tr("=== User Stop Request Received ==="));
- emit logMessageGenerated(tr("Gracefully stopping GA optimization..."));
-
- m_shouldStop = true;
-
- if(m_progressTimer) {
- m_progressTimer->stop();
- }
-
- // 等待当前评估完成,缩短超时时间
- int waitCount = 0;
- while(m_evaluationInProgress > 0 && waitCount < 30) { // 减少等待时间
- QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 50);
- msleep(50);
- waitCount++;
- }
-
- // 超时时强制重置
- if(m_evaluationInProgress > 0) {
- DEBUG_OUT("Force resetting evaluation counter");
- emit logMessageGenerated(tr("Force stopping current evaluation..."));
- m_evaluationInProgress = 0;
- }
-
- // 确保运行标志被清除
- m_isRunning = false;
-
- emit logMessageGenerated(tr("GA optimization stop request processed"));
- DEBUG_OUT("Stop request processed");
- } else {
- DEBUG_OUT("Stop request received but GA is not running");
- emit logMessageGenerated(tr("Stop request received but optimization is not running"));
- }
- cleanupTemporaryDirectory();
-}
-
-bool nmCalculationAutoFitGA::isRunning() const
-{
- return m_isRunning;
-}
-
-int nmCalculationAutoFitGA::getCurrentGeneration() const
-{
- return m_currentGeneration;
-}
-
-QVector nmCalculationAutoFitGA::getBestSolution() const
-{
- return m_bestIndividual.genes;
-}
-
-double nmCalculationAutoFitGA::getBestFitness() const
-{
- return m_bestFitness;
-}
-
-QString nmCalculationAutoFitGA::getLastError() const
-{
- return m_lastError;
-}
-
-void nmCalculationAutoFitGA::resetOptimizer()
-{
- m_population.clear();
- m_eliteIndividuals.clear();
- m_bestIndividual = GAIndividual();
- m_bestFitness = 1e10;
- m_worstFitness = -1e10;
- m_averageFitness = 1e10;
- m_previousBestFitness = 1e10;
- m_currentGeneration = 0;
- m_totalEvaluations = 0;
- m_successfulEvaluations = 0;
- m_convergenceHistory.clear();
- m_lastError.clear();
- m_initialValues.clear();
- m_userInitialSolution.clear();
- m_userInitialFitness = 1e10;
- m_hasValidUserSolution = false;
- m_diversityHistory.clear();
-
- DEBUG_OUT("GA optimizer reset");
-}
-
-void nmCalculationAutoFitGA::setGATargetWellName(const QString& wellName)
-{
- m_targetWellName = wellName;
-}
-
-void nmCalculationAutoFitGA::updateProgress()
-{
- // 这个槽函数在定时器触发时被调用,可以用来更新界面或执行周期性任务
- if(m_isRunning) {
- QApplication::processEvents();
- }
-}
-
-// ==================== 数据加载方法 ====================
-bool nmCalculationAutoFitGA::loadAllConfigFromDataManager()
-{
- try {
- loadOptimizationConfig();
- loadParameterBounds();
- extractUserInitialValues();
- return true;
- } catch(...) {
- m_lastError = "Failed to load configuration from data manager";
- return false;
- }
-}
-
-void nmCalculationAutoFitGA::loadOptimizationConfig()
-{
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
- nmDataAutomaticFitting fittingData = dataManager->getAutomaticFittingDataCopy();
-
- // 加载基本配置
- m_maxGenerations = fittingData.getIterationCount().getValue().toInt();
- m_targetError = fittingData.getErrorTolerance().getValue().toDouble();
-
- // 设置GA默认参数
- m_populationSize = 40;
- m_crossoverRate = 0.8;
- m_mutationRate = 0.1;
- m_elitismRate = 0.15;
- m_tournamentSize = 3;
- m_useUniformCrossover = true;
-
- DEBUG_OUT(QString("Loaded GA optimization config: generations=%1, error=%2, population=%3")
- .arg(m_maxGenerations).arg(m_targetError).arg(m_populationSize));
-}
-
-void nmCalculationAutoFitGA::loadParameterBounds()
-{
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
- nmDataAutomaticFitting fittingData = dataManager->getAutomaticFittingDataCopy();
-
- // 获取参数选择状态
- m_parameterSelected.resize(8);
- m_parameterSelected[0] = fittingData.getPermeabilitySelected();
- m_parameterSelected[1] = fittingData.getSkinSelected();
- m_parameterSelected[2] = fittingData.getWellboreStorageSelected();
- m_parameterSelected[3] = fittingData.getPorositySelected();
- m_parameterSelected[4] = fittingData.getThicknessSelected();
- m_parameterSelected[5] = fittingData.getCtSelected();
- m_parameterSelected[6] = fittingData.getCfSelected();
- m_parameterSelected[7] = fittingData.getSwiSelected();
-
- // 获取参数边界
- m_parameterLower.resize(8);
- m_parameterUpper.resize(8);
-
- m_parameterLower[0] = fittingData.getPermeabilityMin().getValue().toDouble();
- m_parameterUpper[0] = fittingData.getPermeabilityMax().getValue().toDouble();
-
- m_parameterLower[1] = fittingData.getSkinMin().getValue().toDouble();
- m_parameterUpper[1] = fittingData.getSkinMax().getValue().toDouble();
-
- m_parameterLower[2] = fittingData.getWellboreStorageMin().getValue().toDouble();
- m_parameterUpper[2] = fittingData.getWellboreStorageMax().getValue().toDouble();
-
- m_parameterLower[3] = fittingData.getPorosityMin().getValue().toDouble();
- m_parameterUpper[3] = fittingData.getPorosityMax().getValue().toDouble();
-
- m_parameterLower[4] = fittingData.getThicknessMin().getValue().toDouble();
- m_parameterUpper[4] = fittingData.getThicknessMax().getValue().toDouble();
-
- m_parameterLower[5] = fittingData.getCtMin().getValue().toDouble();
- m_parameterUpper[5] = fittingData.getCtMax().getValue().toDouble();
-
- m_parameterLower[6] = fittingData.getCfMin().getValue().toDouble();
- m_parameterUpper[6] = fittingData.getCfMax().getValue().toDouble();
-
- m_parameterLower[7] = fittingData.getSwiMin().getValue().toDouble();
- m_parameterUpper[7] = fittingData.getSwiMax().getValue().toDouble();
-
- // 更新启用参数索引
- m_enabledParamIndices.clear();
- for(int i = 0; i < m_parameterSelected.size(); ++i) {
- if(m_parameterSelected[i]) {
- m_enabledParamIndices.append(i);
- }
- }
- DEBUG_OUT(QString("Loaded parameter bounds: %1 enabled parameters")
- .arg(m_enabledParamIndices.size()));
-}
-
-void nmCalculationAutoFitGA::extractUserInitialValues()
-{
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
- nmDataReservoir reservoirData = dataManager->getReservoirDataCopy();
- //QVector wells = dataManager->getWellDataList();
- nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
-
- m_initialValues.clear();
-
- // 按照启用参数的顺序提取初始值
- for(int i = 0; i < m_enabledParamIndices.size(); ++i) {
- int paramIndex = m_enabledParamIndices[i];
- double initialValue = 0.0;
-
- switch(paramIndex) {
- case 0: // 渗透率
- initialValue = reservoirData.getPermeability().getValue().toDouble();
- break;
-
- case 1: // 表皮系数
- if(pTargetWell) {
- initialValue = pTargetWell->getPerforation(0)->getSkin().getValue().toDouble();
- }
-
- break;
-
- case 2: // 井筒储集系数
- if(pTargetWell) {
- initialValue = pTargetWell->getWellboreStorage().getValue().toDouble();
- }
-
- break;
-
- case 3: // 孔隙度
- initialValue = reservoirData.getPorosity().getValue().toDouble();
- break;
-
- case 4: // 储层厚度
- initialValue = reservoirData.getThickness().getValue().toDouble();
- break;
-
- case 5: // 综合压缩系数
- initialValue = reservoirData.getCt().getValue().toDouble();
- break;
-
- case 6: // 岩石压缩系数
- initialValue = reservoirData.getCf().getValue().toDouble();
- break;
-
- case 7: // 初始含水饱和度
- initialValue = reservoirData.getSwi().getValue().toDouble();
- break;
- }
-
- m_initialValues.append(initialValue);
- }
-
- DEBUG_OUT(QString("Extracted %1 user initial values").arg(m_initialValues.size()));
-
- for(int i = 0; i < m_initialValues.size(); ++i) {
- DEBUG_OUT(QString(" Initial[%1] = %2").arg(i).arg(m_initialValues[i], 0, 'e', 3));
- }
-
- // 验证初始值
- if(!validateInitialValues()) {
- DEBUG_OUT("Warning: Some initial values are outside parameter bounds");
- }
-}
-
-// ==================== 遗传算法核心方法 ====================
-
-void nmCalculationAutoFitGA::initializePopulation()
-{
- int dimensions = getEnabledParameterCount();
- if(dimensions == 0) return;
-
- m_population.clear();
- m_population.resize(m_populationSize);
-
- bool hasValidInitials = !m_initialValues.isEmpty() && m_initialValues.size() >= dimensions;
- int guidedCount = hasValidInitials ? qMax(2, m_populationSize / 2) : qMax(1, m_populationSize / 3);
-
- DEBUG_OUT(QString("Enhanced population initialization: %1 individuals, %2 guided, %3 random")
- .arg(m_populationSize).arg(guidedCount).arg(m_populationSize - guidedCount));
-
- for(int i = 0; i < m_populationSize; ++i) {
- GAIndividual& individual = m_population[i];
- individual.genes.resize(dimensions);
- individual.fitness = 1e10;
- individual.isEvaluated = false;
-
- // 基因初始化
- for(int j = 0; j < dimensions; ++j) {
- int paramIndex = m_enabledParamIndices[j];
- double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
-
- if(i == 0 && hasValidInitials) {
- // 第一个个体:使用用户初始值
- individual.genes[j] = m_initialValues[j];
- } else if(i < guidedCount && hasValidInitials) {
- // 引导搜索策略
- double searchRadius;
- if(i <= guidedCount / 3) {
- searchRadius = range * 0.03;
- } else if(i <= guidedCount * 2 / 3) {
- searchRadius = range * 0.08;
- } else {
- searchRadius = range * 0.15;
- }
- double offset = (random01() - 0.5) * searchRadius;
- individual.genes[j] = m_initialValues[j] + offset;
- } else {
- // 随机初始化
- individual.genes[j] = m_parameterLower[paramIndex] + random01() * range;
- }
- }
-
- // 边界约束
- clampToLimits(individual.genes);
- }
-}
-
-double nmCalculationAutoFitGA::evaluateGenes(const QVector& genes)
-{
- const QString funcName = QString("evaluateGenes[Gen%1]").arg(m_currentGeneration);
- static int callCount = 0;
- callCount++;
-
- try {
- DEBUG_OUT(QString("%1: Call #%2 - Starting evaluation with %3 genes")
- .arg(funcName).arg(callCount).arg(genes.size()));
-
- // 打印参数值用于对比
- QString paramStr = "Parameters: ";
-
- for(int i = 0; i < genes.size(); ++i) {
- paramStr += QString("[%1]=%2 ").arg(i).arg(genes[i], 0, 'f', 6);
- }
-
- DEBUG_OUT(QString("%1: %2").arg(funcName).arg(paramStr));
-
- // 1. 参数有效性检查
- if(!validateParameters(genes)) {
- DEBUG_OUT(QString("%1: Call #%2 - Invalid parameters").arg(funcName).arg(callCount));
- return 1e10;
- }
-
- // 2. 检查数据管理器状态
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
-
- if(!dataManager) {
- DEBUG_OUT(QString("%1: Call #%2 - DataManager is null").arg(funcName).arg(callCount));
- return 1e10;
- }
-
- // 3. 应用参数到数据管理器
- try {
- DEBUG_OUT(QString("%1: Call #%2 - Applying parameters to DataManager").arg(funcName).arg(callCount));
- applyParametersToDataManager(genes);
- DEBUG_OUT(QString("%1: Call #%2 - Parameters applied successfully").arg(funcName).arg(callCount));
- } catch(const std::exception& e) {
- DEBUG_OUT(QString("%1: Call #%2 - Failed to apply parameters: %3").arg(funcName).arg(callCount).arg(e.what()));
- return 1e10;
- } catch(...) {
- DEBUG_OUT(QString("%1: Call #%2 - Unknown error applying parameters").arg(funcName).arg(callCount));
- return 1e10;
- }
-
- // 4. 运行求解器
- QVector > solverResult;
- const int maxRetries = 2;
- bool solverSuccess = false;
-
- for(int retry = 0; retry <= maxRetries; ++retry) {
- if(m_shouldStop) return 1e10;
-
- try {
- DEBUG_OUT(QString("%1: Call #%2 - Solver attempt %3/%4")
- .arg(funcName).arg(callCount).arg(retry + 1).arg(maxRetries + 1));
-
- // 在求解器调用前添加短暂延迟,确保状态稳定
- if(retry > 0) {
- DEBUG_OUT(QString("%1: Call #%2 - Retry delay before solver attempt")
- .arg(funcName).arg(callCount));
- msleep(1000); // 增加延迟时间
- }
-
- solverResult = runSolver();
-
- if(!solverResult.isEmpty() && validateSolverResult(solverResult)) {
- DEBUG_OUT(QString("%1: Call #%2 - Solver successful on attempt %3, result size: %4")
- .arg(funcName).arg(callCount).arg(retry + 1).arg(solverResult[0].size()));
- solverSuccess = true;
- break;
- } else {
- DEBUG_OUT(QString("%1: Call #%2 - Solver failed on attempt %3 - empty or invalid result")
- .arg(funcName).arg(callCount).arg(retry + 1));
-
- if(retry < maxRetries) {
- DEBUG_OUT(QString("%1: Call #%2 - Will retry solver").arg(funcName).arg(callCount));
- }
- }
-
- } catch(const std::exception& e) {
- DEBUG_OUT(QString("%1: Call #%2 - Solver exception on attempt %3: %4")
- .arg(funcName).arg(callCount).arg(retry + 1).arg(e.what()));
- } catch(...) {
- DEBUG_OUT(QString("%1: Call #%2 - Unknown solver exception on attempt %3")
- .arg(funcName).arg(callCount).arg(retry + 1));
- }
- }
-
- if(!solverSuccess) {
- DEBUG_OUT(QString("%1: Call #%2 - All solver attempts failed").arg(funcName).arg(callCount));
- return 1e10;
- }
-
- // 5. 获取双对数结果数据
- QVector > resultLogLogData;
-
- try {
-
- nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
-
- if(pTargetWell) {
- resultLogLogData = pTargetWell->getResultLogLog();
-
- if(!validateLogLogData(resultLogLogData)) {
- DEBUG_OUT(QString("%1: Call #%2 - Invalid result LogLog data").arg(funcName).arg(callCount));
- return 1e10;
- }
-
- DEBUG_OUT(QString("%1: Call #%2 - LogLog result data obtained, size: %3")
- .arg(funcName).arg(callCount).arg(resultLogLogData[0].size()));
- } else {
- DEBUG_OUT(QString("%1: Call #%2 - No wells found after solver").arg(funcName).arg(callCount));
- return 1e10;
- }
- } catch(const std::exception& e) {
- DEBUG_OUT(QString("%1: Call #%2 - Error getting LogLog result: %3").arg(funcName).arg(callCount).arg(e.what()));
- return 1e10;
- } catch(...) {
- DEBUG_OUT(QString("%1: Call #%2 - Unknown error getting LogLog result").arg(funcName).arg(callCount));
- return 1e10;
- }
-
- // 6. 双对数曲线对齐和误差计算
- double error;
-
- try {
- error = calculateLogLogCurveError(m_targetLogLogData, resultLogLogData);
-
- if(!isFiniteNumber(error) || error < 0) {
- DEBUG_OUT(QString("%1: Call #%2 - Invalid error value: %3").arg(funcName).arg(callCount).arg(error));
- return 1e10;
- }
-
- DEBUG_OUT(QString("%1: Call #%2 - Evaluation successful, error = %3")
- .arg(funcName).arg(callCount).arg(error, 0, 'e', 6));
-
- } catch(const std::exception& e) {
- DEBUG_OUT(QString("%1: Call #%2 - LogLog error calculation failed: %3").arg(funcName).arg(callCount).arg(e.what()));
- return 1e10;
- } catch(...) {
- DEBUG_OUT(QString("%1: Call #%2 - Unknown error in LogLog error calculation").arg(funcName).arg(callCount));
- return 1e10;
- }
-
- return error;
-
- } catch(const std::exception& e) {
- DEBUG_OUT(QString("%1: Call #%2 - Top-level exception: %3").arg(funcName).arg(callCount).arg(e.what()));
- return 1e10;
- } catch(...) {
- DEBUG_OUT(QString("%1: Call #%2 - Unknown top-level exception").arg(funcName).arg(callCount));
- return 1e10;
- }
-}
-
-double nmCalculationAutoFitGA::evaluateIndividual(GAIndividual& individual)
-{
- // 如果已经评估过,直接返回
- if(individual.isEvaluated) {
- return individual.fitness;
- }
-
- // 调用新的评估函数
- individual.fitness = evaluateGenes(individual.genes);
- individual.isEvaluated = true;
-
- return individual.fitness;
-}
-
-void nmCalculationAutoFitGA::evaluatePopulation()
-{
- int successfulEvaluations = 0;
- int totalEvaluations = 0;
- int currentGenerationFailed = 0;
-
- for(int i = 0; i < m_population.size() && !m_shouldStop; ++i) {
- GAIndividual& individual = m_population[i];
-
- if(!individual.isEvaluated) {
- try {
- double previousFitness = individual.fitness;
- individual.fitness = evaluateGenes(individual.genes);
- individual.isEvaluated = true;
- totalEvaluations++;
- m_totalEvaluations++;
-
- if(individual.fitness < 1e9) {
- successfulEvaluations++;
- m_successfulEvaluations++;
-
- // 更新全局最优
- if(individual.fitness < m_bestFitness) {
- m_bestFitness = individual.fitness;
- m_bestIndividual = individual;
-
- emit logMessageGenerated(tr(" Individual %1 improved: %2 -> %3")
- .arg(i + 1).arg(previousFitness, 0, 'e', 3).arg(individual.fitness, 0, 'e', 3));
- }
- } else {
- currentGenerationFailed++;
- emit logMessageGenerated(tr(" Individual %1: evaluation failed").arg(i + 1));
- }
-
- // 每评估2个个体处理一次事件
- if(i % 2 == 0) {
- QApplication::processEvents();
- }
-
- } catch(const std::exception& e) {
- emit logMessageGenerated(tr(" Individual %1: Exception: %2").arg(i + 1).arg(e.what()));
- individual.fitness = 1e10;
- individual.isEvaluated = true;
- totalEvaluations++;
- currentGenerationFailed++;
- m_totalEvaluations++;
- } catch(...) {
- emit logMessageGenerated(tr(" Individual %1: Unknown exception").arg(i + 1));
- individual.fitness = 1e10;
- individual.isEvaluated = true;
- totalEvaluations++;
- currentGenerationFailed++;
- m_totalEvaluations++;
- }
- }
- }
-
- if(m_shouldStop) return;
-
- double currentSuccessRate = totalEvaluations > 0 ?
- (double)successfulEvaluations / totalEvaluations : 0.0;
-
- // 更新连续失败代数计数
- if(successfulEvaluations == 0) {
- m_consecutiveFailedGenerations++;
- emit logMessageGenerated(tr("WARNING: No successful evaluations in generation %1 (consecutive failures: %2)")
- .arg(m_currentGeneration + 1).arg(m_consecutiveFailedGenerations));
- } else {
- m_consecutiveFailedGenerations = 0; // 重置连续失败计数
- }
-
- // 输出当前代统计
- emit logMessageGenerated(tr("Current generation stats: %1 successful, %2 failed out of %3 individuals (success rate: %4%)")
- .arg(successfulEvaluations).arg(currentGenerationFailed)
- .arg(totalEvaluations).arg(currentSuccessRate * 100, 0, 'f', 1));
-
- // 检查是否需要停止优化
- if(m_consecutiveFailedGenerations >= m_maxConsecutiveFailures) {
- m_lastError = QString("Too many consecutive failed generations (%1)").arg(m_consecutiveFailedGenerations);
- emit logMessageGenerated(tr("ERROR: Too many consecutive failed generations (%1/%2) - stopping optimization")
- .arg(m_consecutiveFailedGenerations).arg(m_maxConsecutiveFailures));
- return;
- }
-
- // 警告低成功率但不立即停止
- if(currentSuccessRate < 0.5 && m_currentGeneration > 3) {
- emit logMessageGenerated(tr("WARNING: Low success rate (%1%) in generation %2, but continuing optimization")
- .arg(currentSuccessRate * 100, 0, 'f', 1).arg(m_currentGeneration + 1));
- }
-}
-
-int nmCalculationAutoFitGA::tournamentSelection()
-{
- int bestIndex = qrand() % m_population.size();
- double bestFitness = m_population[bestIndex].fitness;
-
- for(int i = 1; i < m_tournamentSize; ++i) {
- int candidateIndex = qrand() % m_population.size();
- double candidateFitness = m_population[candidateIndex].fitness;
-
- if(candidateFitness < bestFitness) {
- bestIndex = candidateIndex;
- bestFitness = candidateFitness;
- }
- }
-
- return bestIndex;
-}
-
-int nmCalculationAutoFitGA::rouletteWheelSelection()
-{
- // 计算适应度总和(使用倒数,因为我们要最小化)
- double totalFitness = 0.0;
- double maxFitness = -1e10;
-
- // 找到最大适应度值
- for(int i = 0; i < m_population.size(); ++i) {
- if(m_population[i].fitness > maxFitness) {
- maxFitness = m_population[i].fitness;
- }
- }
-
- // 计算转换后的适应度总和
- for(int i = 0; i < m_population.size(); ++i) {
- double transformedFitness = maxFitness - m_population[i].fitness + 1e-6;
- totalFitness += transformedFitness;
- }
-
- // 轮盘赌选择
- double randomValue = random01() * totalFitness;
- double cumulativeFitness = 0.0;
-
- for(int i = 0; i < m_population.size(); ++i) {
- double transformedFitness = maxFitness - m_population[i].fitness + 1e-6;
- cumulativeFitness += transformedFitness;
-
- if(cumulativeFitness >= randomValue) {
- return i;
- }
- }
-
- return m_population.size() - 1; // 备用选择
-}
-
-void nmCalculationAutoFitGA::crossover(const GAIndividual& parent1, const GAIndividual& parent2,
- GAIndividual& offspring1, GAIndividual& offspring2)
-{
- if(m_useUniformCrossover) {
- uniformCrossover(parent1, parent2, offspring1, offspring2);
- } else {
- singlePointCrossover(parent1, parent2, offspring1, offspring2);
- }
-}
-
-void nmCalculationAutoFitGA::singlePointCrossover(const GAIndividual& parent1, const GAIndividual& parent2,
- GAIndividual& offspring1, GAIndividual& offspring2)
-{
- int dimensions = parent1.genes.size();
-
- if(dimensions == 0) return;
-
- // 初始化子代
- offspring1.genes.resize(dimensions);
- offspring2.genes.resize(dimensions);
- offspring1.fitness = 1e10;
- offspring2.fitness = 1e10;
- offspring1.isEvaluated = false;
- offspring2.isEvaluated = false;
-
- // 选择交叉点
- int crossoverPoint = qrand() % dimensions;
-
- // 执行交叉
- for(int i = 0; i < dimensions; ++i) {
- if(i < crossoverPoint) {
- offspring1.genes[i] = parent1.genes[i];
- offspring2.genes[i] = parent2.genes[i];
- } else {
- offspring1.genes[i] = parent2.genes[i];
- offspring2.genes[i] = parent1.genes[i];
- }
- }
-}
-
-void nmCalculationAutoFitGA::uniformCrossover(const GAIndividual& parent1, const GAIndividual& parent2,
- GAIndividual& offspring1, GAIndividual& offspring2)
-{
- int dimensions = parent1.genes.size();
-
- if(dimensions == 0) return;
-
- // 初始化子代
- offspring1.genes.resize(dimensions);
- offspring2.genes.resize(dimensions);
- offspring1.fitness = 1e10;
- offspring2.fitness = 1e10;
- offspring1.isEvaluated = false;
- offspring2.isEvaluated = false;
-
- // 均匀交叉
- for(int i = 0; i < dimensions; ++i) {
- if(random01() < 0.5) {
- offspring1.genes[i] = parent1.genes[i];
- offspring2.genes[i] = parent2.genes[i];
- } else {
- offspring1.genes[i] = parent2.genes[i];
- offspring2.genes[i] = parent1.genes[i];
- }
- }
-}
-
-void nmCalculationAutoFitGA::mutate(GAIndividual& individual)
-{
- // 使用高斯变异作为主要方式
- gaussianMutation(individual);
-}
-
-void nmCalculationAutoFitGA::gaussianMutation(GAIndividual& individual)
-{
- for(int i = 0; i < individual.genes.size(); ++i) {
- if(random01() < m_mutationRate) {
- int paramIndex = m_enabledParamIndices[i];
- double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
- double sigma = range * MUTATION_STRENGTH;
-
- // 高斯变异
- double mutation = gaussianRandom(0.0, sigma);
- individual.genes[i] += mutation;
-
- // 边界处理
- individual.genes[i] = qMax(m_parameterLower[paramIndex],
- qMin(m_parameterUpper[paramIndex], individual.genes[i]));
- }
- }
-
- // 标记为未评估
- individual.isEvaluated = false;
-}
-
-void nmCalculationAutoFitGA::polynomialMutation(GAIndividual& individual)
-{
- const double eta = 20.0; // 分布指数
-
- for(int i = 0; i < individual.genes.size(); ++i) {
- if(random01() < m_mutationRate) {
- int paramIndex = m_enabledParamIndices[i];
- double lower = m_parameterLower[paramIndex];
- double upper = m_parameterUpper[paramIndex];
- double y = individual.genes[i];
-
- double delta1 = (y - lower) / (upper - lower);
- double delta2 = (upper - y) / (upper - lower);
-
- double rnd = random01();
- double mut_pow = 1.0 / (eta + 1.0);
-
- double deltaq;
-
- if(rnd <= 0.5) {
- double xy = 1.0 - delta1;
- double val = 2.0 * rnd + (1.0 - 2.0 * rnd) * pow(xy, eta + 1.0);
- deltaq = pow(val, mut_pow) - 1.0;
- } else {
- double xy = 1.0 - delta2;
- double val = 2.0 * (1.0 - rnd) + 2.0 * (rnd - 0.5) * pow(xy, eta + 1.0);
- deltaq = 1.0 - pow(val, mut_pow);
- }
-
- y += deltaq * (upper - lower);
- individual.genes[i] = qMax(lower, qMin(upper, y));
- }
- }
-
- // 标记为未评估
- individual.isEvaluated = false;
-}
-
-void nmCalculationAutoFitGA::applyElitism(QVector& newPopulation)
-{
- int eliteCount = static_cast(m_populationSize * m_elitismRate);
-
- if(eliteCount == 0) return;
-
- // 对种群按适应度排序
- QVector sortedPopulation = m_population;
-
- // 冒泡排序(适应度从小到大)
- for(int i = 0; i < sortedPopulation.size() - 1; ++i) {
- for(int j = 0; j < sortedPopulation.size() - 1 - i; ++j) {
- if(sortedPopulation[j].fitness > sortedPopulation[j + 1].fitness) {
- GAIndividual temp = sortedPopulation[j];
- sortedPopulation[j] = sortedPopulation[j + 1];
- sortedPopulation[j + 1] = temp;
- }
- }
- }
-
- // 复制精英个体到新种群
- for(int i = 0; i < eliteCount && i < sortedPopulation.size(); ++i) {
- newPopulation.append(sortedPopulation[i]);
- }
-
- DEBUG_OUT(QString("Applied elitism: %1 elite individuals preserved").arg(eliteCount));
-}
-
-void nmCalculationAutoFitGA::updatePopulationStatistics()
-{
- if(m_population.isEmpty()) return;
-
- m_previousBestFitness = m_bestFitness;
-
- double sum = 0.0;
- double minFitness = 1e10;
- double maxFitness = -1e10;
- int validCount = 0;
-
- for(int i = 0; i < m_population.size(); ++i) {
- const GAIndividual& individual = m_population[i];
-
- if(individual.isEvaluated && individual.fitness < 1e9) {
- sum += individual.fitness;
- validCount++;
-
- if(individual.fitness < minFitness) {
- minFitness = individual.fitness;
- }
-
- if(individual.fitness > maxFitness) {
- maxFitness = individual.fitness;
- }
-
- // 更新全局最优
- if(individual.fitness < m_bestFitness) {
- // 只有显著改进时才更新
- double improvement = (m_bestFitness - individual.fitness);
- double relativeImprovement = improvement / qMax(1e-10, qAbs(m_bestFitness));
-
- if(relativeImprovement > m_improvementThreshold) {
- m_bestFitness = individual.fitness;
- m_bestIndividual = individual;
-
- DEBUG_OUT(QString("Global best updated with %1% improvement: %2")
- .arg(relativeImprovement * 100, 0, 'f', 3)
- .arg(m_bestFitness, 0, 'e', 4));
- }
- }
- }
- }
-
- if(validCount > 0) {
- m_averageFitness = sum / validCount;
- m_worstFitness = maxFitness;
- } else {
- m_averageFitness = 1e10;
- m_worstFitness = 1e10;
- }
-
- // 在没有找到更好解时才检查精英保护
- if(m_hasValidUserSolution && m_userInitialFitness < m_bestFitness) {
- DEBUG_OUT("Elite protection: No significant improvement found, checking initial solution");
-
- // 检查初始解是否仍然是最优的
- double improvement = m_bestFitness - m_userInitialFitness;
- double relativeImprovement = improvement / qMax(1e-10, qAbs(m_bestFitness));
-
- if(relativeImprovement > m_improvementThreshold * 0.5) { // 使用更宽松的阈值
- DEBUG_OUT("Elite protection: Restoring user initial solution");
- m_bestFitness = m_userInitialFitness;
- m_bestIndividual.genes = m_userInitialSolution;
- m_bestIndividual.fitness = m_userInitialFitness;
- m_bestIndividual.isEvaluated = true;
- }
- }
-}
-
-bool nmCalculationAutoFitGA::checkConvergence()
-{
- if(m_convergenceHistory.size() < CONVERGENCE_CHECK_INTERVAL) {
- return false;
- }
-
- // 检查最近几次世代的改进
- double recentBest = m_convergenceHistory.last();
- double oldBest = m_convergenceHistory[m_convergenceHistory.size() - CONVERGENCE_CHECK_INTERVAL];
-
- double improvement = oldBest - recentBest;
-
- // 如果连续多代没有显著改进,认为已收敛
- if(improvement < MIN_FITNESS_IMPROVEMENT) {
- // 检查是否连续停滞
- int stagnationCount = 0;
-
- for(int i = m_convergenceHistory.size() - 1; i >= qMax(0, m_convergenceHistory.size() - MAX_STAGNATION_GENERATIONS); --i) {
- if(i > 0) {
- double diff = m_convergenceHistory[i - 1] - m_convergenceHistory[i];
-
- if(diff < MIN_FITNESS_IMPROVEMENT) {
- stagnationCount++;
- } else {
- break;
- }
- }
- }
-
- return stagnationCount >= MAX_STAGNATION_GENERATIONS;
- }
-
- return false;
-}
-
-void nmCalculationAutoFitGA::adaptiveParameterUpdate(int generation)
-{
- // 自适应调整变异率
- double progress = static_cast(generation) / m_maxGenerations;
-
- // 早期探索,后期开发
- m_mutationRate = 0.2 * (1.0 - progress) + 0.05 * progress;
-
- // 自适应调整交叉率
- if(generation > 0) {
- double improvement = m_previousBestFitness - m_bestFitness;
-
- if(improvement < MIN_FITNESS_IMPROVEMENT) {
- // 如果改进很小,增加探索性
- m_mutationRate = qMin(0.3, m_mutationRate * 1.1);
- m_crossoverRate = qMax(0.6, m_crossoverRate * 0.95);
- } else {
- // 如果有明显改进,增加开发性
- m_mutationRate = qMax(0.05, m_mutationRate * 0.9);
- m_crossoverRate = qMin(0.9, m_crossoverRate * 1.05);
- }
- }
-}
-
-void nmCalculationAutoFitGA::validateAndProtectFinalResult()
-{
- if(!m_hasValidUserSolution) {
- emit logMessageGenerated(tr("No initial solution for elite protection"));
- return;
- }
-
- emit logMessageGenerated(tr("=== Final Result Validation (Elite Protection) ==="));
-
- // 使用已有的评估结果
- double finalFitness = m_bestFitness;
- double initialFitness = m_userInitialFitness;
-
- emit logMessageGenerated(tr("Comparing results: Initial=%1, Final=%2")
- .arg(initialFitness, 0, 'e', 4).arg(finalFitness, 0, 'e', 4));
-
- // 计算改进程度
- double improvement = initialFitness - finalFitness;
- double relativeImprovement = improvement / qMax(1e-10, qAbs(initialFitness));
-
- emit logMessageGenerated(tr("Improvement: %1 (%2%)")
- .arg(improvement, 0, 'e', 4).arg(relativeImprovement * 100, 0, 'f', 2));
-
- if(relativeImprovement < m_improvementThreshold) {
- emit logMessageGenerated(tr("Elite protection triggered: insufficient improvement"));
- emit logMessageGenerated(tr("Threshold: %1%, Actual: %2%")
- .arg(m_improvementThreshold * 100, 0, 'f', 2)
- .arg(relativeImprovement * 100, 0, 'f', 4));
- emit logMessageGenerated(tr("Restoring initial solution as final result"));
-
- m_bestFitness = initialFitness;
- m_bestIndividual.genes = m_userInitialSolution;
- m_bestIndividual.fitness = initialFitness;
- m_bestIndividual.isEvaluated = true;
-
- emit logMessageGenerated(tr("Initial solution restored successfully"));
- } else {
- emit logMessageGenerated(tr("Final result validated - significant improvement achieved"));
- }
-}
-
-StopReasonGA nmCalculationAutoFitGA::analyzeOptimizationStatus()
-{
- // 1. 检查用户停止
- if(m_shouldStop) {
- return GA_USER_STOPPED;
- }
-
- // 2. 检查连续失败
- if(m_consecutiveFailedGenerations >= m_maxConsecutiveFailures) {
- return GA_CONSECUTIVE_FAILURES;
- }
-
- // 3. 检查是否达到目标精度
- if(m_bestFitness < m_targetError) {
- return GA_TARGET_ACHIEVED;
- }
-
- // 4. 检查是否达到最大迭代数
- if(m_currentGeneration >= m_maxGenerations - 1) {
- return GA_MAX_ITERATIONS;
- }
-
- // 5. 需要足够的历史数据才能判断收敛
- if(m_convergenceHistory.size() < m_localOptimumWindow) {
- return GA_CONTINUE_OPTIMIZATION;
- }
-
- // 6. 检查真正的收敛
- if(m_convergenceHistory.size() >= m_trueConvergenceWindow && checkTrueConvergence()) {
- return GA_TRUE_CONVERGENCE;
- }
-
- // 7. 检查局部最优陷阱
- if(checkLocalOptimumTrap()) {
- return GA_LOCAL_OPTIMUM;
- }
-
- return GA_CONTINUE_OPTIMIZATION;
-}
-
-bool nmCalculationAutoFitGA::checkTrueConvergence() const
-{
- if(m_convergenceHistory.size() < m_trueConvergenceWindow) {
- return false;
- }
-
- // 1. 检查解质量 - 如果已经接近目标,小改进可能是真收敛
- bool nearTarget = (m_bestFitness < m_targetError * m_nearTargetFactor);
-
- // 2. 检查适应度稳定性 - 长期小幅波动
- double recentVariance = calculateFitnessVariance(10);
- double recentMean = 0.0;
- int windowSize = qMin(10, m_convergenceHistory.size());
-
- // 计算最近窗口的均值
- for(int i = m_convergenceHistory.size() - windowSize; i < m_convergenceHistory.size(); ++i) {
- recentMean += m_convergenceHistory[i];
- }
-
- recentMean /= windowSize;
-
- double relativeVariance = recentVariance / qMax(1e-10, recentMean * recentMean);
- bool stableError = (relativeVariance < m_convergenceVarianceThreshold);
-
- // 3. 检查种群多样性 - 应该收敛到同一区域
- double currentDiversity = calculatePopulationDiversity();
- bool lowDiversity = (currentDiversity < m_diversityThreshold);
-
- // 4. 检查长期改进趋势
- double longTermImprovement = calculateLongTermImprovement(m_trueConvergenceWindow);
- bool minimalLongTermImprovement = (longTermImprovement < 1e-4); // 0.01%
-
- // 真收敛的判断条件
- bool isConverged = nearTarget ||
- (stableError && lowDiversity && minimalLongTermImprovement);
-
- if(isConverged) {
- DEBUG_OUT("=== TRUE CONVERGENCE ANALYSIS ===");
- DEBUG_OUT(QString("nearTarget=%1 (fitness=%2, target*factor=%3)")
- .arg(nearTarget).arg(m_bestFitness, 0, 'e', 4)
- .arg(m_targetError * m_nearTargetFactor, 0, 'e', 4));
- DEBUG_OUT(QString("stableError=%1 (relativeVariance=%2)")
- .arg(stableError).arg(relativeVariance, 0, 'e', 6));
- DEBUG_OUT(QString("lowDiversity=%1 (diversity=%2, threshold=%3)")
- .arg(lowDiversity).arg(currentDiversity, 0, 'f', 6).arg(m_diversityThreshold));
- DEBUG_OUT(QString("longTermImprovement=%1%")
- .arg(longTermImprovement * 100, 0, 'f', 4));
- }
-
- return isConverged;
-}
-
-bool nmCalculationAutoFitGA::checkLocalOptimumTrap() const
-{
- if(m_convergenceHistory.size() < m_localOptimumWindow) {
- return false;
- }
-
- // 1. 检查解质量 - 如果距离目标还很远,停滞就可能是局部最优
- bool farFromTarget = (m_bestFitness > m_targetError * m_farTargetFactor);
-
- // 2. 检查短期改进 - 近期改进非常小
- double shortTermImprovement = calculateLongTermImprovement(m_localOptimumWindow);
- bool poorShortTermImprovement = (shortTermImprovement < 1e-5); // 0.001%
-
- // 3. 检查种群多样性 - 可能过早聚集或无效分散
- double currentDiversity = calculatePopulationDiversity();
- bool problematicDiversity = (currentDiversity < m_diversityThreshold * 0.1) ||
- (currentDiversity > m_diversityThreshold * 5.0);
-
- // 4. 检查适应度方差 - 可能卡在平坦区域
- double fitnessVariance = calculateFitnessVariance(m_localOptimumWindow);
- bool flatFitnessLandscape = (fitnessVariance < m_convergenceVarianceThreshold * 0.1);
-
- // 局部最优的判断条件
- bool isLocalOptimum = farFromTarget && poorShortTermImprovement &&
- (problematicDiversity || flatFitnessLandscape);
-
- if(isLocalOptimum) {
- DEBUG_OUT("=== LOCAL OPTIMUM ANALYSIS ===");
- DEBUG_OUT(QString("farFromTarget=%1 (fitness=%2, target*factor=%3)")
- .arg(farFromTarget).arg(m_bestFitness, 0, 'e', 4)
- .arg(m_targetError * m_farTargetFactor, 0, 'e', 4));
- DEBUG_OUT(QString("poorShortTermImprovement=%1 (improvement=%2%)")
- .arg(poorShortTermImprovement).arg(shortTermImprovement * 100, 0, 'f', 4));
- DEBUG_OUT(QString("problematicDiversity=%1 (diversity=%2)")
- .arg(problematicDiversity).arg(currentDiversity, 0, 'f', 6));
- DEBUG_OUT(QString("fitnessVariance=%1")
- .arg(fitnessVariance, 0, 'e', 6));
- }
-
- return isLocalOptimum;
-}
-
-double nmCalculationAutoFitGA::calculatePopulationDiversity() const
-{
- if(m_population.size() < 2) return 0.0;
-
- int dimensions = getEnabledParameterCount();
-
- if(dimensions == 0) return 0.0;
-
- double totalDiversity = 0.0;
-
- for(int dim = 0; dim < dimensions; ++dim) {
- // 计算该维度上所有个体的均值
- double mean = 0.0;
-
- for(int i = 0; i < m_population.size(); ++i) {
- mean += m_population[i].genes[dim];
- }
-
- mean /= m_population.size();
-
- // 计算该维度上的方差
- double variance = 0.0;
-
- for(int i = 0; i < m_population.size(); ++i) {
- double diff = m_population[i].genes[dim] - mean;
- variance += diff * diff;
- }
-
- variance /= m_population.size();
-
- // 归一化到参数范围
- int paramIndex = m_enabledParamIndices[dim];
- double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
- double normalizedStd = sqrt(variance) / qMax(1e-10, range);
-
- totalDiversity += normalizedStd;
- }
-
- return totalDiversity / dimensions;
-}
-
-double nmCalculationAutoFitGA::calculateFitnessVariance(int windowSize) const
-{
- if(m_convergenceHistory.size() < windowSize) {
- return 1e10; // 数据不足,返回大值
- }
-
- // 计算最近windowSize次迭代的方差
- double mean = 0.0;
- int startIdx = m_convergenceHistory.size() - windowSize;
-
- for(int i = startIdx; i < m_convergenceHistory.size(); ++i) {
- mean += m_convergenceHistory[i];
- }
-
- mean /= windowSize;
-
- double variance = 0.0;
-
- for(int i = startIdx; i < m_convergenceHistory.size(); ++i) {
- double diff = m_convergenceHistory[i] - mean;
- variance += diff * diff;
- }
-
- variance /= windowSize;
-
- return variance;
-}
-
-double nmCalculationAutoFitGA::calculateLongTermImprovement(int windowSize) const
-{
- if(m_convergenceHistory.size() < windowSize) {
- return 1.0; // 数据不足,假设有改进
- }
-
- double oldFitness = m_convergenceHistory[m_convergenceHistory.size() - windowSize];
- double improvement = (oldFitness - m_bestFitness) / qMax(1e-10, qAbs(oldFitness));
-
- return improvement;
-}
-
-void nmCalculationAutoFitGA::updateConvergenceMetrics()
-{
- // 更新多样性历史
- m_diversityHistory.append(calculatePopulationDiversity());
-
- // 保持历史长度合理(最多保留50个数据点)
- const int maxHistorySize = 50;
-
- while(m_diversityHistory.size() > maxHistorySize) {
- m_diversityHistory.remove(0);
- }
-}
-
-// ==================== 参数应用方法 ====================
-
-bool nmCalculationAutoFitGA::validateParameters(const QVector& parameters) const
-{
- if(parameters.size() != getEnabledParameterCount()) {
- return false;
- }
-
- for(int i = 0; i < parameters.size(); ++i) {
- if(!isFiniteNumber(parameters[i])) {
- return false;
- }
-
- // 检查参数范围
- if(i < m_enabledParamIndices.size()) {
- int paramIndex = m_enabledParamIndices[i];
-
- if(paramIndex >= 0 && paramIndex < m_parameterLower.size()) {
- if(parameters[i] < m_parameterLower[paramIndex] ||
- parameters[i] > m_parameterUpper[paramIndex]) {
- return false;
- }
- }
- }
- }
-
- // 直接拦截会导致求解器数值崩溃的参数值
- for(int i = 0; i < parameters.size() && i < m_enabledParamIndices.size(); ++i) {
- int paramIndex = m_enabledParamIndices[i];
- double value = parameters[i];
-
- switch(paramIndex) {
- case 0: // 渗透率:必须大于零
- if(value <= 1e-8) {
- DEBUG_OUT(QString("Rejecting near-zero permeability: %1").arg(value));
- return false;
- }
-
- break;
-
- case 2: // 井筒储集系数:必须大于零
- if(value <= 1e-10) {
- DEBUG_OUT(QString("Rejecting near-zero wellbore storage: %1").arg(value));
- return false;
- }
-
- break;
-
- case 3: // 孔隙度:必须在合理范围
- if(value <= 1e-6 || value >= 0.99) {
- DEBUG_OUT(QString("Rejecting unrealistic porosity: %1").arg(value));
- return false;
- }
-
- break;
-
- case 5: // 综合压缩系数:必须大于零
- if(value <= 1e-8) {
- DEBUG_OUT(QString("Rejecting near-zero total compressibility: %1").arg(value));
- return false;
- }
-
- break;
- }
- }
-
- return true;
-}
-
-bool nmCalculationAutoFitGA::validateLogLogData(const QVector>& logLogData) const
-{
- // 检查基本结构
- if(logLogData.size() < 3) {
- DEBUG_OUT("LogLog data has less than 3 arrays");
- return false;
- }
-
- // 检查数组大小一致性
- int size = logLogData[0].size();
-
- if(size == 0) {
- DEBUG_OUT("Empty LogLog data");
- return false;
- }
-
- if(logLogData[1].size() != size || logLogData[2].size() != size) {
- DEBUG_OUT(QString("LogLog data size mismatch: X=%1, Y1=%2, Y2=%3")
- .arg(logLogData[0].size())
- .arg(logLogData[1].size())
- .arg(logLogData[2].size()));
- return false;
- }
-
- // 检查最小数据点数
- if(size < 5) {
- DEBUG_OUT(QString("Too few LogLog data points: %1").arg(size));
- return false;
- }
-
- // 数据有效性检查
- for(int i = 0; i < size; ++i) {
- if(!isFiniteNumber(logLogData[0][i]) ||
- !isFiniteNumber(logLogData[1][i]) ||
- !isFiniteNumber(logLogData[2][i])) {
- DEBUG_OUT(QString("Invalid LogLog data at index %1").arg(i));
- return false;
- }
- }
-
- return true;
-}
-
-bool nmCalculationAutoFitGA::validateInitialValues() const
-{
- if(m_initialValues.size() != m_enabledParamIndices.size()) {
- DEBUG_OUT("Initial values count mismatch with enabled parameters");
- return false;
- }
-
- bool allValid = true;
-
- for(int i = 0; i < m_initialValues.size(); ++i) {
- int paramIndex = m_enabledParamIndices[i];
- double value = m_initialValues[i];
-
- if(!isFiniteNumber(value)) {
- DEBUG_OUT(QString("Initial value[%1] is not finite: %2").arg(i).arg(value));
- allValid = false;
- continue;
- }
-
- if(paramIndex < m_parameterLower.size() && paramIndex < m_parameterUpper.size()) {
- double minVal = m_parameterLower[paramIndex];
- double maxVal = m_parameterUpper[paramIndex];
-
- if(value < minVal || value > maxVal) {
- DEBUG_OUT(QString("Initial value[%1] = %2 is outside bounds [%3, %4]")
- .arg(i).arg(value).arg(minVal).arg(maxVal));
- allValid = false;
- }
- }
- }
-
- return allValid;
-}
-
-void nmCalculationAutoFitGA::applyParametersToDataManager(const QVector& parameters)
-{
- if(parameters.size() != getEnabledParameterCount()) {
- return;
- }
-
- updateReservoirParameters(parameters);
- updateWellParameters(parameters);
-}
-
-void nmCalculationAutoFitGA::updateReservoirParameters(const QVector& parameters)
-{
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
- nmDataReservoir reservoirData = dataManager->getReservoirDataCopy();
-
- int paramIndex = 0;
- for(int i = 0; i < m_parameterSelected.size(); ++i) {
- if(m_parameterSelected[i] && paramIndex < parameters.size()) {
- double value = parameters[paramIndex];
-
- switch(i) {
- case 0: // 渗透率
- reservoirData.getPermeability().setValue(value);
- break;
- case 3: // 孔隙度
- reservoirData.getPorosity().setValue(value);
- break;
- case 4: // 储层厚度
- reservoirData.getThickness().setValue(value);
- break;
- case 5: // 综合压缩系数
- reservoirData.getCt().setValue(value);
- break;
- case 6: // 岩石压缩系数
- reservoirData.getCf().setValue(value);
- break;
- case 7: // 初始含水饱和度
- reservoirData.getSwi().setValue(value);
- break;
- }
- paramIndex++;
- }
- }
-
- // 更新数据管理器
- dataManager->updateReservoirData(reservoirData);
-}
-
-void nmCalculationAutoFitGA::updateWellParameters(const QVector& parameters)
-{
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
-
- // 获取井列表,更新第一口井的参数
- //QVector wells = dataManager->getWellDataList();
- nmDataWellBase* pWell = dataManager->findWellByName(m_targetWellName);
- if(!pWell) return;
-
- //nmDataWellBase* pWell = wells[0]; // 使用第一口井
-
- int paramIndex = 0;
- for(int i = 0; i < m_parameterSelected.size(); ++i) {
- if(m_parameterSelected[i] && paramIndex < parameters.size()) {
- double value = parameters[paramIndex];
-
- switch(i) {
- case 1: { // 表皮系数
- nmDataAttribute skinAttr = pWell->getPerforation(0)->getSkin();
- skinAttr.setValue(value);
- pWell->setRateDependentSkin(skinAttr);
- }
- break;
- case 2: { // 井筒储集系数
- nmDataAttribute wellboreAttr = pWell->getWellboreStorage();
- wellboreAttr.setValue(value);
- pWell->setWellboreStorage(wellboreAttr);
- }
- break;
- }
- paramIndex++;
- }
- }
-
- // 根据井类型更新到数据管理器
- updateWellToDataManager(pWell);
-}
-
-void nmCalculationAutoFitGA::updateWellToDataManager(nmDataWellBase* pWell)
-{
- if(!pWell) return;
-
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
- NM_WELL_MODEL wellType = pWell->getWellType();
-
- switch(wellType) {
- case NM_WELL_MODEL::Vertical_Well: {
- nmDataVerticalWell* pVerticalWell = dynamic_cast(pWell);
- if(pVerticalWell) {
- QVector wells;
- wells.append(*pVerticalWell);
- dataManager->updateVerticalWells(wells);
- }
- break;
- }
- case NM_WELL_MODEL::Vertical_Fractured_Well: {
- nmDataVerticalFracturedWell* pVFracturedWell = dynamic_cast(pWell);
- if(pVFracturedWell) {
- QVector wells;
- wells.append(*pVFracturedWell);
- dataManager->updateVerticalFracturedWells(wells);
- }
- break;
- }
- case NM_WELL_MODEL::Horizontal_Fractured_Well: {
- nmDataHorizontalFracturedWell* pHFracturedWell = dynamic_cast(pWell);
- if(pHFracturedWell) {
- QVector wells;
- wells.append(*pHFracturedWell);
- dataManager->updateHorizontalFracturedWells(wells);
- }
- break;
- }
- default:
- break;
- }
-}
-
-void nmCalculationAutoFitGA::clampToLimits(QVector& parameters) const
-{
- for(int i = 0; i < parameters.size() && i < m_enabledParamIndices.size(); ++i) {
- int paramIndex = m_enabledParamIndices[i];
- if(paramIndex >= 0 && paramIndex < m_parameterLower.size()) {
- parameters[i] = qMax(m_parameterLower[paramIndex],
- qMin(m_parameterUpper[paramIndex], parameters[i]));
- }
- }
-}
-
-// ==================== 求解器相关方法 ====================
-
-QVector> nmCalculationAutoFitGA::runSolver()
-{
- //return runSolverExe();
- return runSolverDll();
-}
-
-bool nmCalculationAutoFitGA::validateSolverResult(const QVector>& result) const
-{
- // 基本检查
- if(result.size() < 2) {
- DEBUG_OUT("Solver result has less than 2 arrays");
- return false;
- }
-
- if(result[0].size() != result[1].size()) {
- DEBUG_OUT(QString("Size mismatch: X=%1, Y=%2").arg(result[0].size()).arg(result[1].size()));
- return false;
- }
-
- if(result[0].size() == 0) {
- DEBUG_OUT("Empty solver result");
- return false;
- }
-
- // 检查最小数据点数
- if(result[0].size() < 10) {
- DEBUG_OUT(QString("Too few data points: %1").arg(result[0].size()));
- return false;
- }
-
- // 数据有效性检查
- for(int i = 0; i < result[0].size(); ++i) {
- if(!isFiniteNumber(result[0][i]) || !isFiniteNumber(result[1][i])) {
- DEBUG_OUT(QString("Invalid data at index %1: X=%2, Y=%3")
- .arg(i).arg(result[0][i]).arg(result[1][i]));
- return false;
- }
- }
-
- return true;
-}
-
-QVector> nmCalculationAutoFitGA::runSolverDll()
-{
- DEBUG_OUT("SOLVER DLL START");
-
- if(m_evaluationInProgress > 0) {
- DEBUG_OUT("DLL Solver already running, skipping");
- return QVector>();
- }
-
- ++m_evaluationInProgress;
- QVector> result;
- nmCalculationDllPebiSolverTask* dllTask = nullptr;
-
- try {
- DEBUG_OUT("Creating DLL solver task");
- dllTask = new nmCalculationDllPebiSolverTask(m_tempDirectory);
-
- if(m_shouldStop) {
- DEBUG_OUT("Should stop - cleaning up and returning empty result");
- delete dllTask;
- --m_evaluationInProgress;
- return result;
- }
-
- DEBUG_OUT("Starting DLL solver execution...");
-
- // 异步执行
- dllTask->start();
-
- // 等待完成
- int waitTime = 0;
- const int maxWait = 30000; // 30秒超时
- const int checkInterval = 500;
-
- while(waitTime < maxWait) {
- QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 100);
-
- if(!dllTask->isRunning()) {
- DEBUG_OUT("DLL solver task completed");
- break;
- }
-
- if(m_shouldStop) {
- DEBUG_OUT("DLL solver task terminated by user");
- dllTask->terminate();
- break;
- }
-
- msleep(checkInterval);
- waitTime += checkInterval;
- }
-
- // 超时处理
- if(dllTask->isRunning()) {
- DEBUG_OUT("DLL solver task timeout, terminating...");
- dllTask->terminate();
- dllTask->wait(2000);
-
- delete dllTask;
- dllTask = nullptr;
- --m_evaluationInProgress;
- m_consecutiveFailures++;
- return result;
- }
-
- // 验证结果数据是否已更新
- nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
- //QVector wells = dataManager->getWellDataList();
- nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
-
- if(!pTargetWell) {
- DEBUG_OUT("No wells found in data manager after DLL execution");
- delete dllTask;
- --m_evaluationInProgress;
- return result;
- }
-
- // 验证结果数据
- QVector> pressureResult = pTargetWell->getResultPressure();
- QVector> logLogResult = pTargetWell->getResultLogLog();
-
- DEBUG_OUT(QString("DLL result verification - Pressure arrays: %1, LogLog arrays: %2")
- .arg(pressureResult.size()).arg(logLogResult.size()));
-
- if(pressureResult.size() >= 2) {
- DEBUG_OUT(QString("Pressure result - Time points: %1, Pressure points: %2")
- .arg(pressureResult[0].size()).arg(pressureResult[1].size()));
-
- if(pressureResult[0].size() > 0) {
- DEBUG_OUT(QString("Sample pressure data - Time[0]: %1, Time[last]: %2, P[0]: %3, P[last]: %4")
- .arg(pressureResult[0][0])
- .arg(pressureResult[0][pressureResult[0].size()-1])
- .arg(pressureResult[1][0])
- .arg(pressureResult[1][pressureResult[1].size()-1]));
- }
- }
-
- // 数据有效性检查
- if(pressureResult.size() >= 2 && pressureResult[0].size() > 0 && pressureResult[1].size() > 0) {
- result = pressureResult;
- DEBUG_OUT(QString("Got DLL solver result: %1 points").arg(result[0].size()));
- m_consecutiveFailures = 0;
-
- // 检查结果是否与之前不同
- static QVector lastPressureResult;
- bool isDifferentFromLast = false;
-
- if(lastPressureResult.isEmpty() || lastPressureResult.size() != pressureResult[1].size()) {
- isDifferentFromLast = true;
- } else {
- for(int i = 0; i < qMin(5, pressureResult[1].size()); ++i) {
- if(qAbs(lastPressureResult[i] - pressureResult[1][i]) > 1e-12) {
- isDifferentFromLast = true;
- break;
- }
- }
- }
-
- if(isDifferentFromLast) {
- DEBUG_OUT("RESULT VERIFICATION: Got NEW result data from DLL");
- lastPressureResult = pressureResult[1];
- } else {
- DEBUG_OUT("!!! WARNING: Result data appears to be identical to previous run !!!");
- }
-
- } else {
- DEBUG_OUT("DLL solver result is empty or invalid");
- DEBUG_OUT(QString("Pressure result size: %1, Array sizes: %2, %3")
- .arg(pressureResult.size())
- .arg(pressureResult.size() > 0 ? pressureResult[0].size() : 0)
- .arg(pressureResult.size() > 1 ? pressureResult[1].size() : 0));
- m_consecutiveFailures++;
- }
-
- } catch(const std::bad_alloc& e) {
- DEBUG_OUT(QString("Memory allocation failed in DLL solver: %1").arg(e.what()));
- m_consecutiveFailures++;
- } catch(const std::exception& e) {
- DEBUG_OUT(QString("Exception in DLL solver: %1").arg(e.what()));
- m_consecutiveFailures++;
- } catch(...) {
- DEBUG_OUT("Unknown exception in DLL solver");
- m_consecutiveFailures++;
- }
-
- // 清理DLL任务
- if(dllTask) {
- if(dllTask->isRunning()) {
- dllTask->terminate();
- dllTask->wait(3000);
- }
-
- DEBUG_OUT("Cleaning up DLL solver task...");
- delete dllTask;
- dllTask = nullptr;
- }
-
- QApplication::processEvents(QEventLoop::AllEvents, 100);
- --m_evaluationInProgress;
-
- DEBUG_OUT(QString("SOLVER DLL END - ResultPoints: %1")
- .arg(result.isEmpty() ? 0 : result[0].size()));
-
- return result;
-}
-
-//QVector> nmCalculationAutoFitGA::runSolverExe()
-//{
-// DEBUG_OUT("SOLVER EXE START");
-//
-// if(m_evaluationInProgress > 0) {
-// DEBUG_OUT("EXE Solver already running, skipping");
-// return QVector>();
-// }
-//
-// ++m_evaluationInProgress;
-// QVector> result;
-// nmCalculationExeSolverTask* exeTask = nullptr;
-//
-// try {
-// DEBUG_OUT("Creating EXE solver task");
-// exeTask = new nmCalculationExeSolverTask(QString());
-//
-// if(m_shouldStop) {
-// DEBUG_OUT("Should stop - cleaning up and returning empty result");
-// delete exeTask;
-// --m_evaluationInProgress;
-// return result;
-// }
-//
-// DEBUG_OUT("Starting EXE solver execution...");
-//
-// // 启动
-// bool executeSuccess = exeTask->execute();
-//
-// // 检查执行状态
-// if(!executeSuccess) {
-// DEBUG_OUT(QString("EXE solver execution failed: %1").arg(exeTask->getLastError()));
-// DEBUG_OUT(QString("EXE solver exit code: %1").arg(exeTask->getExitCode()));
-//
-// // 清理并返回空结果
-// delete exeTask;
-// exeTask = nullptr;
-// --m_evaluationInProgress;
-// m_consecutiveFailures++;
-// return result;
-// }
-//
-// DEBUG_OUT("EXE solver execution completed successfully");
-//
-// // 验证结果数据是否已更新
-// nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
-// QVector wells = dataManager->getWellDataList();
-//
-// if(wells.isEmpty()) {
-// DEBUG_OUT("No wells found in data manager after EXE execution");
-// delete exeTask;
-// --m_evaluationInProgress;
-// return result;
-// }
-//
-// // 验证结果数据的时间戳或唯一性
-// QVector> pressureResult = wells[0]->getResultPressure();
-// QVector> logLogResult = wells[0]->getResultLogLog();
-//
-// DEBUG_OUT(QString("Raw result verification - Pressure arrays: %1, LogLog arrays: %2")
-// .arg(pressureResult.size()).arg(logLogResult.size()));
-//
-// if(pressureResult.size() >= 2) {
-// DEBUG_OUT(QString("Pressure result - Time points: %1, Pressure points: %2")
-// .arg(pressureResult[0].size()).arg(pressureResult[1].size()));
-//
-// if(pressureResult[0].size() > 0) {
-// DEBUG_OUT(QString("Sample pressure data - Time[0]: %1, Time[last]: %2, P[0]: %3, P[last]: %4")
-// .arg(pressureResult[0][0])
-// .arg(pressureResult[0][pressureResult[0].size() - 1])
-// .arg(pressureResult[1][0])
-// .arg(pressureResult[1][pressureResult[1].size() - 1]));
-// }
-// }
-//
-// if(logLogResult.size() >= 3) {
-// DEBUG_OUT(QString("LogLog result - X points: %1, Y1 points: %2, Y2 points: %3")
-// .arg(logLogResult[0].size()).arg(logLogResult[1].size()).arg(logLogResult[2].size()));
-//
-// if(logLogResult[0].size() > 0) {
-// DEBUG_OUT(QString("Sample LogLog data - X[0]: %1, X[last]: %2, Y1[0]: %3, Y2[0]: %4")
-// .arg(logLogResult[0][0])
-// .arg(logLogResult[0][logLogResult[0].size() - 1])
-// .arg(logLogResult[1][0])
-// .arg(logLogResult[2][0]));
-// }
-// }
-//
-// // 数据有效性检查
-// if(pressureResult.size() >= 2 && pressureResult[0].size() > 0 && pressureResult[1].size() > 0) {
-// result = pressureResult;
-// DEBUG_OUT(QString("Got EXE solver result: %1 points").arg(result[0].size()));
-// m_consecutiveFailures = 0;
-//
-// // 检查结果是否与之前不同
-// static QVector lastPressureResult;
-// bool isDifferentFromLast = false;
-//
-// if(lastPressureResult.isEmpty() || lastPressureResult.size() != pressureResult[1].size()) {
-// isDifferentFromLast = true;
-// } else {
-// for(int i = 0; i < qMin(5, pressureResult[1].size()); ++i) {
-// if(qAbs(lastPressureResult[i] - pressureResult[1][i]) > 1e-12) {
-// isDifferentFromLast = true;
-// break;
-// }
-// }
-// }
-//
-// if(isDifferentFromLast) {
-// DEBUG_OUT("RESULT VERIFICATION: Got NEW result data from EXE");
-// lastPressureResult = pressureResult[1];
-// } else {
-// DEBUG_OUT("!!! WARNING: Result data appears to be identical to previous run !!!");
-// }
-//
-// } else {
-// DEBUG_OUT("EXE solver result is empty or invalid");
-// DEBUG_OUT(QString("Pressure result size: %1, Array sizes: %2, %3")
-// .arg(pressureResult.size())
-// .arg(pressureResult.size() > 0 ? pressureResult[0].size() : 0)
-// .arg(pressureResult.size() > 1 ? pressureResult[1].size() : 0));
-// m_consecutiveFailures++;
-// }
-//
-// } catch(const std::exception& e) {
-// DEBUG_OUT(QString("Exception in EXE solver: %1").arg(e.what()));
-// m_consecutiveFailures++;
-// } catch(...) {
-// DEBUG_OUT("Unknown exception in EXE solver");
-// m_consecutiveFailures++;
-// }
-//
-// // 清理EXE任务
-// if(exeTask) {
-// DEBUG_OUT("Cleaning up EXE solver task...");
-// delete exeTask;
-// exeTask = nullptr;
-// }
-//
-// QApplication::processEvents(QEventLoop::AllEvents, 100);
-// --m_evaluationInProgress;
-//
-// DEBUG_OUT(QString("SOLVER EXE END - ResultPoints: %1")
-// .arg(result.isEmpty() ? 0 : result[0].size()));
-//
-// return result;
-//}
-
-// ==================== 数据处理方法 ====================
-QVector nmCalculationAutoFitGA::interpolateData(
- const QVector& source, const QVector& targetX) const
-{
- QVector result;
-
- if(source.isEmpty() || targetX.isEmpty()) {
- DEBUG_OUT("Warning: Empty data in interpolation");
- return result;
- }
-
- // 数据清理和验证合并
- QVector validSource;
- const double MAX_REASONABLE_VALUE = 1e12;
-
- for(int i = 0; i < source.size(); ++i) {
- const QPointF& point = source[i];
-
- // 检查数值有效性
- if(!isFiniteNumber(point.x()) || !isFiniteNumber(point.y())) {
- DEBUG_OUT(QString("Skipping invalid data point at index %1: X=%2, Y=%3")
- .arg(i).arg(point.x()).arg(point.y()));
- continue;
- }
-
- // 检查极大值 - 跳过而不是失败
- if(qAbs(point.y()) > MAX_REASONABLE_VALUE) {
- DEBUG_OUT(QString("Skipping extremely large Y value at index %1: %2")
- .arg(i).arg(point.y()));
- continue;
- }
-
- // 只保留有效的数据点
- validSource.append(point);
- }
-
- // 检查清理后的数据是否足够
- if(validSource.size() < 3) {
- DEBUG_OUT(QString("Insufficient valid data points after cleaning: %1")
- .arg(validSource.size()));
- return result; // 返回空结果,但不算失败
- }
-
- DEBUG_OUT(QString("Data cleaning: %1 -> %2 valid points")
- .arg(source.size()).arg(validSource.size()));
-
- // 对清理后的数据进行排序
- QVector sortedSource = validSource;
-
- for(int i = 0; i < sortedSource.size() - 1; ++i) {
- for(int j = 0; j < sortedSource.size() - 1 - i; ++j) {
- if(sortedSource[j].x() > sortedSource[j + 1].x()) {
- QPointF temp = sortedSource[j];
- sortedSource[j] = sortedSource[j + 1];
- sortedSource[j + 1] = temp;
- }
- }
- }
-
- double sourceMinX = sortedSource.first().x();
- double sourceMaxX = sortedSource.last().x();
-
- double targetMinX = targetX[0];
- double targetMaxX = targetX[0];
-
- for(int i = 1; i < targetX.size(); ++i) {
- if(targetX[i] < targetMinX) targetMinX = targetX[i];
-
- if(targetX[i] > targetMaxX) targetMaxX = targetX[i];
- }
-
- DEBUG_OUT(QString("Source X range: [%1, %2], Target X range: [%3, %4]")
- .arg(sourceMinX).arg(sourceMaxX).arg(targetMinX).arg(targetMaxX));
-
- // 安全插值算法
- for(int i = 0; i < targetX.size(); ++i) {
- double x = targetX[i];
-
- if(!isFiniteNumber(x)) continue;
-
- double y = 0.0;
-
- // 插值逻辑(保持原有逻辑,但使用sortedSource)
- if(x <= sourceMinX) {
- if(sortedSource.size() >= 2) {
- double dx = sortedSource[1].x() - sortedSource[0].x();
-
- if(qAbs(dx) > 1e-10) {
- double slope = (sortedSource[1].y() - sortedSource[0].y()) / dx;
- slope = qMax(-1e6, qMin(1e6, slope));
- y = sortedSource[0].y() + slope * (x - sortedSource[0].x());
- } else {
- y = sortedSource[0].y();
- }
- } else {
- y = sortedSource[0].y();
- }
- } else if(x >= sourceMaxX) {
- if(sortedSource.size() >= 2) {
- int lastIdx = sortedSource.size() - 1;
- double dx = sortedSource[lastIdx].x() - sortedSource[lastIdx - 1].x();
-
- if(qAbs(dx) > 1e-10) {
- double slope = (sortedSource[lastIdx].y() - sortedSource[lastIdx - 1].y()) / dx;
- slope = qMax(-1e6, qMin(1e6, slope));
- y = sortedSource[lastIdx].y() + slope * (x - sortedSource[lastIdx].x());
- } else {
- y = sortedSource[lastIdx].y();
- }
- } else {
- y = sortedSource.last().y();
- }
- } else {
- // 内插
- bool found = false;
-
- for(int j = 0; j < sortedSource.size() - 1; ++j) {
- if(x >= sortedSource[j].x() && x <= sortedSource[j + 1].x()) {
- double dx = sortedSource[j + 1].x() - sortedSource[j].x();
-
- if(qAbs(dx) > 1e-10) {
- double ratio = (x - sortedSource[j].x()) / dx;
- y = sortedSource[j].y() + ratio * (sortedSource[j + 1].y() - sortedSource[j].y());
- } else {
- y = sortedSource[j].y();
- }
-
- found = true;
- break;
- }
- }
-
- if(!found) {
- // 使用最近点
- double minDist = 1e10;
-
- for(int k = 0; k < sortedSource.size(); ++k) {
- double dist = qAbs(sortedSource[k].x() - x);
-
- if(dist < minDist) {
- minDist = dist;
- y = sortedSource[k].y();
- }
- }
- }
- }
-
- // 最终数值检查
- if(!isFiniteNumber(y)) {
- y = sortedSource.size() > 0 ? sortedSource[0].y() : 1.0;
- }
-
- y = qMax(-1e12, qMin(1e12, y));
-
- result.append(QPointF(x, y));
- }
-
- if(result.isEmpty()) {
- DEBUG_OUT("LogLog interpolation failed");
- return result;
- }
-
- DEBUG_OUT(QString("Interpolation completed: %1 -> %2 points")
- .arg(validSource.size()).arg(result.size()));
-
- return result;
-}
-
-double nmCalculationAutoFitGA::calculateLogLogCurveError(
- const QVector>& target,
- const QVector>& result) const
-{
- // 验证数据
- if(!validateLogLogData(target) || !validateLogLogData(result)) {
- return 1e10;
- }
-
- try {
- // 数据对齐:找到X值的重叠区域
- double targetMinX = target[0][0];
- double targetMaxX = target[0][0];
-
- for(int i = 1; i < target[0].size(); ++i) {
- if(target[0][i] < targetMinX) targetMinX = target[0][i];
-
- if(target[0][i] > targetMaxX) targetMaxX = target[0][i];
- }
-
- double resultMinX = result[0][0];
- double resultMaxX = result[0][0];
-
- for(int i = 1; i < result[0].size(); ++i) {
- if(result[0][i] < resultMinX) resultMinX = result[0][i];
-
- if(result[0][i] > resultMaxX) resultMaxX = result[0][i];
- }
-
- double overlapMinX = qMax(targetMinX, resultMinX);
- double overlapMaxX = qMin(targetMaxX, resultMaxX);
-
- if(overlapMinX >= overlapMaxX) {
- DEBUG_OUT("No overlap between target and result LogLog curves");
- return 1e10;
- }
-
- // 生成公共X网格进行插值
- QVector commonX;
- int numPoints = 50;
-
- if(overlapMinX > 0 && overlapMaxX > 0) {
- // 对数空间均匀分布
- double logMin = qLn(overlapMinX);
- double logMax = qLn(overlapMaxX);
-
- for(int i = 0; i < numPoints; ++i) {
- double logX = logMin + i * (logMax - logMin) / (numPoints - 1);
- double x = qExp(logX);
-
- // 数值保护
- if(!isFiniteNumber(x) || x <= 0) {
- continue;
- }
-
- commonX.append(x);
- }
-
- DEBUG_OUT("Using log-uniform grid for better early-time coverage");
- }
-
- if(commonX.isEmpty()) {
- DEBUG_OUT("Failed to generate common X grid");
- return 1e10;
- }
-
- // 插值目标曲线
- QVector targetCurve1, targetCurve2;
-
- for(int i = 0; i < target[0].size(); ++i) {
- // 检查数据有效性
- if(isFiniteNumber(target[0][i]) && isFiniteNumber(target[1][i]) &&
- isFiniteNumber(target[2][i])) {
- targetCurve1.append(QPointF(target[0][i], target[1][i]));
- targetCurve2.append(QPointF(target[0][i], target[2][i]));
- }
- }
-
- if(targetCurve1.isEmpty() || targetCurve2.isEmpty()) {
- DEBUG_OUT("Target curves are empty after filtering");
- return 1e10;
- }
-
- QVector alignedTarget1 = interpolateData(targetCurve1, commonX);
- QVector alignedTarget2 = interpolateData(targetCurve2, commonX);
-
- // 插值结果曲线
- QVector resultCurve1, resultCurve2;
-
- for(int i = 0; i < result[0].size(); ++i) {
- // 检查数据有效性
- if(isFiniteNumber(result[0][i]) && isFiniteNumber(result[1][i]) &&
- isFiniteNumber(result[2][i])) {
- resultCurve1.append(QPointF(result[0][i], result[1][i]));
- resultCurve2.append(QPointF(result[0][i], result[2][i]));
- }
- }
-
- if(resultCurve1.isEmpty() || resultCurve2.isEmpty()) {
- DEBUG_OUT("Result curves are empty after filtering");
- return 1e10;
- }
-
- QVector alignedResult1 = interpolateData(resultCurve1, commonX);
- QVector alignedResult2 = interpolateData(resultCurve2, commonX);
-
- // 检查插值结果
- if(alignedTarget1.isEmpty() || alignedTarget2.isEmpty() ||
- alignedResult1.isEmpty() || alignedResult2.isEmpty()) {
- DEBUG_OUT("LogLog interpolation failed");
- return 1e10;
- }
-
- if(alignedTarget1.size() != alignedResult1.size() ||
- alignedTarget2.size() != alignedResult2.size()) {
- DEBUG_OUT("LogLog interpolation size mismatch");
- return 1e10;
- }
-
- // 计算两条曲线的误差
- double error1 = calculateCurveError(alignedTarget1, alignedResult1);
- double error2 = calculateCurveError(alignedTarget2, alignedResult2);
-
- // 检查个别误差是否有效
- if(!isFiniteNumber(error1) || error1 > 1e9) {
- DEBUG_OUT(QString("Curve1 error is invalid: %1").arg(error1));
- error1 = 1e10;
- }
-
- if(!isFiniteNumber(error2) || error2 > 1e9) {
- DEBUG_OUT(QString("Curve2 error is invalid: %1").arg(error2));
- error2 = 1e10;
- }
-
- // 组合误差 - 添加保护
- double combinedError;
-
- if(error1 > 1e9 && error2 > 1e9) {
- combinedError = 1e10;
- } else if(error1 > 1e9) {
- combinedError = error2;
- } else if(error2 > 1e9) {
- combinedError = error1;
- } else {
- combinedError = 0.5 * error1 + 0.5 * error2;
- }
-
- DEBUG_OUT(QString("LogLog errors: Curve1=%1, Curve2=%2, Combined=%3")
- .arg(error1, 0, 'e', 4).arg(error2, 0, 'e', 4).arg(combinedError, 0, 'e', 4));
-
- return qMin(1e9, combinedError);
-
- } catch(const std::exception& e) {
- DEBUG_OUT(QString("Exception in LogLog error calculation: %1").arg(e.what()));
- return 1e10;
- } catch(...) {
- DEBUG_OUT("Unknown exception in LogLog error calculation");
- return 1e10;
- }
-}
-
-double nmCalculationAutoFitGA::calculateCurveError(
- const QVector& curve1, const QVector& curve2) const
-{
- if(curve1.size() != curve2.size() || curve1.isEmpty()) {
- return 1e10;
- }
-
- // 预检查:确保没有无穷大值
- for(int i = 0; i < curve1.size(); ++i) {
- if(!isFiniteNumber(curve1[i].y()) || !isFiniteNumber(curve2[i].y())) {
- DEBUG_OUT(QString("Infinite value detected at index %1: Y1=%2, Y2=%3")
- .arg(i).arg(curve1[i].y()).arg(curve2[i].y()));
- return 1e10;
- }
-
- if(qAbs(curve1[i].y()) > 1e12 || qAbs(curve2[i].y()) > 1e12) {
- DEBUG_OUT(QString("Extremely large value detected at index %1")
- .arg(i));
- return 1e10;
- }
- }
-
- double totalError = 0.0;
- double totalWeight = 0.0;
- int validPoints = 0;
-
- for(int i = 0; i < curve1.size(); ++i) {
- double y1 = curve1[i].y();
- double y2 = curve2[i].y();
-
- // 跳过异常值
- if(!isFiniteNumber(y1) || !isFiniteNumber(y2)) {
- continue;
- }
-
- // 自适应权重:根据Y值大小调整,添加上限
- double weightFactor = qMin(100.0, qAbs(y1) * 0.01);
- double weight = 1.0 / (1.0 + weightFactor);
-
- // 相对误差和绝对误差的组合
- double yMax = qMax(qAbs(y1), qAbs(y2));
- yMax = qMax(1e-12, yMax); // 防止除零
-
- double relativeError = qAbs(y1 - y2) / yMax;
- double absoluteError = qAbs(y1 - y2);
-
- // 限制误差值
- relativeError = qMin(1e6, relativeError);
- absoluteError = qMin(1e6, absoluteError);
-
- // 误差组合:相对误差为主,绝对误差为辅
- double pointError = 0.7 * relativeError + 0.3 * absoluteError;
-
- if(isFiniteNumber(pointError) && pointError < 1e10) {
- totalError += weight * pointError * pointError;
- totalWeight += weight;
- validPoints++;
- }
- }
-
- if(totalWeight > 0 && validPoints > 0) {
- double result = sqrt(totalError / totalWeight);
-
- // 最终检查
- if(!isFiniteNumber(result)) {
- DEBUG_OUT("Final error calculation produced infinite result");
- return 1e10;
- }
-
- return qMin(1e9, result); // 限制最大误差值
- } else {
- DEBUG_OUT(QString("No valid points for error calculation: validPoints=%1")
- .arg(validPoints));
- return 1e10;
- }
-}
-// ==================== 工具方法 ====================
-
-double nmCalculationAutoFitGA::random01() const
-{
- return static_cast(qrand()) / RAND_MAX;
-}
-
-double nmCalculationAutoFitGA::gaussianRandom(double mean, double stddev) const
-{
- static bool hasSpare = false;
- static double spare;
-
- if(hasSpare) {
- hasSpare = false;
- return spare * stddev + mean;
- }
-
- hasSpare = true;
- double u = qMax(random01(), 1e-12);
- double v = random01();
- double mag = stddev * sqrt(-2.0 * log(u));
- spare = mag * cos(2.0 * 3.14159265359 * v);
-
- return mag * sin(2.0 * 3.14159265359 * v) + mean;
-}
-
-int nmCalculationAutoFitGA::getEnabledParameterCount() const
-{
- int count = 0;
-
- for(int i = 0; i < m_parameterSelected.size(); ++i) {
- if(m_parameterSelected[i]) count++;
- }
-
- return count;
-}
-
-void nmCalculationAutoFitGA::saveOptimizationResult()
-{
- DEBUG_OUT(QString("GA optimization result: fitness=%1, evaluations=%2/%3")
- .arg(m_bestFitness, 0, 'e', 4)
- .arg(m_successfulEvaluations)
- .arg(m_totalEvaluations));
-}
diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp
index d0deda2..cb510f6 100644
--- a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp
+++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp
@@ -104,6 +104,17 @@ static inline bool isFiniteNumber(double value)
#endif
}
+// 两个 RMSE 的差不能直接解释为被消除的独立误差。RMSE 的平方才对应
+// 均方能量,因此先计算 reduced^2-full^2,再开方恢复原量纲。这里用于分别
+// 提取“消除公共上下偏差”和“消除水平位移”实际减少的误差贡献。
+static double nestedRmsContribution(double reducedModelLoss,
+ double fullModelLoss)
+{
+ return qSqrt(qMax(0.0,
+ reducedModelLoss * reducedModelLoss -
+ fullModelLoss * fullModelLoss));
+}
+
static inline bool isInClosedRange(double value, double lower, double upper)
{
return isFiniteNumber(value) && value >= lower && value <= upper;
@@ -421,7 +432,7 @@ static QString findExecutableInPath(const QString& executableName)
static QStringList traceParameterNames()
{
// trace 和 trace meta 使用的完整参数名顺序。
- // 这个顺序必须与 buildTraceParameterVector() 和 m_parameterSelected 的 0-7 索引一致。
+ // 这个顺序必须与 buildTraceParameterVector() 和 m_parameterSelected 的 0-9 索引一致。
QStringList names;
names << "k"
<< "skin"
@@ -430,7 +441,9 @@ static QStringList traceParameterNames()
<< "h"
<< "Ct"
<< "Cf"
- << "Swi";
+ << "Swi"
+ << "Dfc"
+ << "fractureHalfLength";
return names;
}
@@ -721,8 +734,8 @@ void nmCalculationAutoFitPSO::setTargetLogLogData(const QVector
void nmCalculationAutoFitPSO::stopFitting()
{
// 用户点击停止时走这里。停止策略是“请求式停止”:
- // 先置 m_shouldStop,让主循环/求解器等待逻辑自然退出;短时间内还在评价时再重置计数。
- // 这样可以减少 DLL 任务被硬中断导致的数据状态残留。
+ // 只置 m_shouldStop,让主循环/求解器等待逻辑退出并自行维护任务计数。
+ // 不在这里清理临时目录或强制清零计数,避免与正在返回的 DLL 任务竞争。
if(m_simulationMode && m_simulationTimer) {
m_simulationTimer->stop();
}
@@ -736,25 +749,28 @@ void nmCalculationAutoFitPSO::stopFitting()
m_shouldStop = true;
- // 等待当前评估完成,缩短超时时间
+ if(m_simulationMode) {
+ m_isRunning = false;
+ closeTraceFile();
+ cleanupTemporaryDirectory();
+ emit logMessageGenerated(tr("PSO simulation stop request processed"));
+ return;
+ }
+
+ // 给当前评价一个短暂的自然退出时间。若仍在运行,
+ // runSolverDll() 会在下一个等待周期检查 m_shouldStop 并结束任务。
int waitCount = 0;
- while(m_evaluationInProgress > 0 && waitCount < 30) { // 减少等待时间
+ while(m_evaluationInProgress > 0 && waitCount < 30) {
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 50);
msleep(50);
waitCount++;
}
- // 超时时强制重置
if(m_evaluationInProgress > 0) {
- DEBUG_OUT("Force resetting evaluation counter");
- emit logMessageGenerated(tr("Force stopping current evaluation..."));
- m_evaluationInProgress = 0;
+ emit logMessageGenerated(tr("Waiting for current solver evaluation to stop..."));
}
- // 确保运行标志被清除
- m_isRunning = false;
-
emit logMessageGenerated(tr("PSO optimization stop request processed"));
DEBUG_OUT("Stop request processed");
} else {
@@ -762,8 +778,6 @@ void nmCalculationAutoFitPSO::stopFitting()
emit logMessageGenerated(tr("Stop request received but optimization is not running"));
}
- closeTraceFile();
- cleanupTemporaryDirectory();
}
bool nmCalculationAutoFitPSO::isRunning() const
@@ -790,6 +804,12 @@ double nmCalculationAutoFitPSO::getBestFitness() const
return m_globalBestFitness;
}
+AutoFitObjectiveBreakdown nmCalculationAutoFitPSO::getLastObjectiveBreakdown() const
+{
+ // 返回最近一次损失评价的误差分解,供界面或后续优化逻辑读取。
+ return m_lastObjectiveBreakdown;
+}
+
QString nmCalculationAutoFitPSO::getLastError() const
{
// 上一次失败的人类可读错误信息,主要给 UI 层弹窗或日志使用。
@@ -806,9 +826,12 @@ void nmCalculationAutoFitPSO::resetOptimizer()
m_globalBestPosition.clear();
m_globalBestFitness = 1e10;
m_previousBestFitness = 1e10;
+ m_globalBestObjectiveBreakdown = AutoFitObjectiveBreakdown();
m_lastEvaluatedLogLogData.clear();
m_globalBestLogLogData.clear();
+ m_lastObjectiveBreakdown = AutoFitObjectiveBreakdown();
m_userInitialLogLogData.clear();
+ m_userInitialObjectiveBreakdown = AutoFitObjectiveBreakdown();
m_currentIteration = 0;
m_totalEvaluations = 0;
m_successfulEvaluations = 0;
@@ -836,9 +859,8 @@ void nmCalculationAutoFitPSO::setPSOTargetWellName(const QString& wellName)
void nmCalculationAutoFitPSO::initializeTraceFile()
{
- // 创建本次 PSO 的可复盘文件:
- // - pso_baseline_trace_.csv:逐代逐粒子的参数、真实误差、代理误差和筛选决策;
- // - pso_baseline_trace_.meta.json:目标曲线、流量制度、参数上下界、PSO/代理配置。
+ // 创建本次自动拟合的可复盘文件。代理 PSO 与非代理信赖域使用不同前缀,
+ // 防止代理回放脚本把信赖域记录误当成最新 PSO 粒子记录。
//
// 代理模型评分脚本也会读取 meta.json,因此 trace meta 不是单纯日志,而是C++ 与 Python 代理模型之间的运行上下文契约。
if(!m_traceEnabled) {
@@ -857,8 +879,13 @@ void nmCalculationAutoFitPSO::initializeTraceFile()
return;
}
- m_traceFilePath = traceDir.absoluteFilePath(QString("pso_baseline_trace_%1.csv").arg(m_traceRunId));
- m_traceMetaFilePath = traceDir.absoluteFilePath(QString("pso_baseline_trace_%1.meta.json").arg(m_traceRunId));
+ QString tracePrefix = isSurrogateScreeningEnabled()
+ ? "pso_baseline_trace"
+ : "trust_region_trace";
+ m_traceFilePath = traceDir.absoluteFilePath(
+ QString("%1_%2.csv").arg(tracePrefix).arg(m_traceRunId));
+ m_traceMetaFilePath = traceDir.absoluteFilePath(
+ QString("%1_%2.meta.json").arg(tracePrefix).arg(m_traceRunId));
m_traceFile.setFileName(m_traceFilePath);
if(!m_traceFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
@@ -870,11 +897,12 @@ void nmCalculationAutoFitPSO::initializeTraceFile()
writeTraceHeader();
writeTraceMetaFile();
- DEBUG_OUT(QString("PSO baseline trace initialized: %1").arg(m_traceFilePath));
- emit logMessageGenerated(tr("PSO baseline trace: %1").arg(m_traceFilePath));
+ DEBUG_OUT(QString("Automatic fitting trace initialized: %1").arg(m_traceFilePath));
+ emit logMessageGenerated(tr("Automatic fitting trace: %1").arg(m_traceFilePath));
if(!m_traceMetaFilePath.isEmpty()) {
- emit logMessageGenerated(tr("PSO baseline trace meta: %1").arg(m_traceMetaFilePath));
+ emit logMessageGenerated(
+ tr("Automatic fitting trace meta: %1").arg(m_traceMetaFilePath));
}
emit logMessageGenerated(tr("PSO surrogate screening: %1, model=%2, keep=%3, audit=%4, warmup=%5, min_solver=%6")
@@ -985,11 +1013,13 @@ void nmCalculationAutoFitPSO::closeTraceFile()
void nmCalculationAutoFitPSO::writeTraceHeader()
{
// trace CSV 字段说明:
- // - 当前粒子参数只记录代理模型关心的 k/skin/wellboreC/phi/h/Ct/Cf;
+ // - 代理模式保持原 k/skin/wellboreC/phi/h/Ct/Cf 契约;
+ // - 非代理模式额外记录 Swi/Dfc/裂缝半长,便于复盘信赖域调整;
// - solver_objective 是真实求解器误差;
// - surrogate_objective 是 Python 代理评分;
// - screening_decision 说明该粒子为什么跑/不跑真实求解器;
- // - pbest/gbest 字段用于离线复盘 PSO 更新是否只依赖真实误差。
+ // - pbest/gbest 字段用于离线复盘 PSO 更新是否只依赖真实误差;
+ // - 末尾诊断字段记录同一次真实评价的分量误差,便于核对引导方向和接受结果。
if(!m_traceFile.isOpen()) {
return;
}
@@ -1005,8 +1035,13 @@ void nmCalculationAutoFitPSO::writeTraceHeader()
<< "phi"
<< "h"
<< "Ct"
- << "Cf"
- << "solver_objective"
+ << "Cf";
+ if(!isSurrogateScreeningEnabled()) {
+ cols << "Swi"
+ << "Dfc"
+ << "fractureHalfLength";
+ }
+ cols << "solver_objective"
<< "solver_success"
<< "elapsed_ms"
<< "surrogate_objective"
@@ -1018,16 +1053,45 @@ void nmCalculationAutoFitPSO::writeTraceHeader()
<< "pbest_phi"
<< "pbest_h"
<< "pbest_Ct"
- << "pbest_Cf"
- << "gbest_objective"
+ << "pbest_Cf";
+ if(!isSurrogateScreeningEnabled()) {
+ cols << "pbest_Swi"
+ << "pbest_Dfc"
+ << "pbest_fractureHalfLength";
+ }
+ cols << "gbest_objective"
<< "gbest_k"
<< "gbest_skin"
<< "gbest_wellboreC"
<< "gbest_phi"
<< "gbest_h"
<< "gbest_Ct"
- << "gbest_Cf"
- << "enabled_param_indices";
+ << "gbest_Cf";
+ if(!isSurrogateScreeningEnabled()) {
+ cols << "gbest_Swi"
+ << "gbest_Dfc"
+ << "gbest_fractureHalfLength";
+ }
+ cols << "enabled_param_indices"
+ << "pressure_loss"
+ << "derivative_loss";
+ if(!isSurrogateScreeningEnabled()) {
+ cols << "vertical_common_bias";
+ }
+ cols << "vertical_loss";
+ if(!isSurrogateScreeningEnabled()) {
+ cols << "vertical_reliable"
+ << "horizontal_physical_shift";
+ }
+ cols << "horizontal_loss";
+ if(!isSurrogateScreeningEnabled()) {
+ cols << "horizontal_reliable";
+ }
+ cols << "shape_loss"
+ << "late_trend_loss"
+ << "late_slope_bias"
+ << "late_trend_reliable"
+ << "registration_ambiguous";
QTextStream out(&m_traceFile);
out << cols.join(",") << "\n";
@@ -1069,8 +1133,14 @@ void nmCalculationAutoFitPSO::writeTraceMetaFile()
QTextStream out(&metaFile);
out << "{\n";
- out << " \"schema_version\": 1,\n";
- out << " \"trace_type\": \"pso_baseline_replay_meta\",\n";
+ // 非代理 v5 增加带符号诊断列;代理 PSO 保留原 v3 字段和目标,避免改变
+ // 已有模型的训练和回放契约。
+ out << " \"schema_version\": "
+ << (isSurrogateScreeningEnabled() ? 3 : 7) << ",\n";
+ out << " \"trace_type\": "
+ << jsonEscape(isSurrogateScreeningEnabled()
+ ? "pso_baseline_replay_meta"
+ : "diagnostic_trust_region_meta") << ",\n";
out << " \"run_id\": " << jsonEscape(m_traceRunId) << ",\n";
out << " \"created_at\": " << jsonEscape(QDateTime::currentDateTime().toString(Qt::ISODate)) << ",\n";
out << " \"trace_csv\": " << jsonEscape(QFileInfo(m_traceFilePath).fileName()) << ",\n";
@@ -1127,10 +1197,10 @@ void nmCalculationAutoFitPSO::writeTraceMetaFile()
QVector nmCalculationAutoFitPSO::buildTraceParameterVector(const QVector& selectedParameters) const
{
- // 将粒子内部使用的“启用参数向量”还原成完整 8 维参数向量。
+ // 将粒子内部使用的“启用参数向量”还原成完整 10 维参数向量。
// 未启用的参数从当前 DataManager 读取,启用的参数用 selectedParameters 覆盖。
// trace CSV、候选 CSV、代理训练域检查都需要这个完整向量。
- QVector fullParams(8, 0.0);
+ QVector fullParams(10, 0.0);
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
@@ -1146,8 +1216,28 @@ QVector nmCalculationAutoFitPSO::buildTraceParameterVector(const QVector
nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
if(pTargetWell) {
- fullParams[1] = pTargetWell->getPerforation(0)->getSkin().getValue().toDouble();
+ nmDataPerforation* perforation = pTargetWell->getPerforation(0);
+ if(perforation) {
+ fullParams[1] = perforation->getSkin().getValue().toDouble();
+ }
fullParams[2] = pTargetWell->getWellboreStorage().getValue().toDouble();
+
+ // Dfc 只存在于两类压裂井,普通井在完整向量中保持为 0。
+ if(pTargetWell->getWellType() == NM_WELL_MODEL::Vertical_Fractured_Well) {
+ nmDataVerticalFracturedWell* fracturedWell =
+ dynamic_cast(pTargetWell);
+ if(fracturedWell) {
+ fullParams[8] = fracturedWell->getDfc().getValue().toDouble();
+ fullParams[9] = fracturedWell->getFractureHalfLength().getValue().toDouble();
+ }
+ } else if(pTargetWell->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) {
+ nmDataHorizontalFracturedWell* fracturedWell =
+ dynamic_cast(pTargetWell);
+ if(fracturedWell) {
+ fullParams[8] = fracturedWell->getDfc().getValue().toDouble();
+ fullParams[9] = fracturedWell->getFractureHalfLength().getValue().toDouble();
+ }
+ }
}
}
@@ -1172,7 +1262,8 @@ void nmCalculationAutoFitPSO::writeTraceRow(int generation,
double surrogateObjective,
const QString& screeningDecision,
const QVector& pbestPosition,
- double pbestObjective)
+ double pbestObjective,
+ const AutoFitObjectiveBreakdown* objectiveBreakdown)
{
// 写一行 trace。generation=-1/particleIndex=-1 表示用户初始解;
// 普通粒子行的 phase 为 particle_solver、particle_verified_cache 或 particle_not_evaluated。
@@ -1202,8 +1293,13 @@ void nmCalculationAutoFitPSO::writeTraceRow(int generation,
<< traceParamAt(currentParams, 3)
<< traceParamAt(currentParams, 4)
<< traceParamAt(currentParams, 5)
- << traceParamAt(currentParams, 6)
- << traceNumber(solverObjective)
+ << traceParamAt(currentParams, 6);
+ if(!isSurrogateScreeningEnabled()) {
+ cols << traceParamAt(currentParams, 7)
+ << traceParamAt(currentParams, 8)
+ << traceParamAt(currentParams, 9);
+ }
+ cols << traceNumber(solverObjective)
<< QString::number(solverSuccess ? 1 : 0)
<< QString::number(elapsedMs)
<< traceNumber(surrogateObjective)
@@ -1215,16 +1311,55 @@ void nmCalculationAutoFitPSO::writeTraceRow(int generation,
<< traceParamAt(pbestParams, 3)
<< traceParamAt(pbestParams, 4)
<< traceParamAt(pbestParams, 5)
- << traceParamAt(pbestParams, 6)
- << traceNumber(m_globalBestFitness)
+ << traceParamAt(pbestParams, 6);
+ if(!isSurrogateScreeningEnabled()) {
+ cols << traceParamAt(pbestParams, 7)
+ << traceParamAt(pbestParams, 8)
+ << traceParamAt(pbestParams, 9);
+ }
+ cols << traceNumber(m_globalBestFitness)
<< traceParamAt(gbestParams, 0)
<< traceParamAt(gbestParams, 1)
<< traceParamAt(gbestParams, 2)
<< traceParamAt(gbestParams, 3)
<< traceParamAt(gbestParams, 4)
<< traceParamAt(gbestParams, 5)
- << traceParamAt(gbestParams, 6)
- << csvEscape(enabledIndices.join(";"));
+ << traceParamAt(gbestParams, 6);
+ if(!isSurrogateScreeningEnabled()) {
+ cols << traceParamAt(gbestParams, 7)
+ << traceParamAt(gbestParams, 8)
+ << traceParamAt(gbestParams, 9);
+ }
+ cols << csvEscape(enabledIndices.join(";"));
+
+ if(objectiveBreakdown && objectiveBreakdown->valid) {
+ cols << traceNumber(objectiveBreakdown->pressureLoss)
+ << traceNumber(objectiveBreakdown->derivativeLoss);
+ if(!isSurrogateScreeningEnabled()) {
+ cols << traceNumber(objectiveBreakdown->verticalCommonBias);
+ }
+ cols << traceNumber(objectiveBreakdown->verticalLoss);
+ if(!isSurrogateScreeningEnabled()) {
+ cols << QString::number(objectiveBreakdown->verticalReliable ? 1 : 0)
+ << traceNumber(objectiveBreakdown->horizontalPhysicalShift);
+ }
+ cols << traceNumber(objectiveBreakdown->horizontalLoss);
+ if(!isSurrogateScreeningEnabled()) {
+ cols << QString::number(objectiveBreakdown->horizontalReliable ? 1 : 0);
+ }
+ cols << traceNumber(objectiveBreakdown->shapeLoss)
+ << traceNumber(objectiveBreakdown->lateDerivativeTrendLoss)
+ << traceNumber(objectiveBreakdown->lateDerivativeSlopeBias)
+ << QString::number(objectiveBreakdown->lateDerivativeTrendReliable ? 1 : 0)
+ << QString::number(objectiveBreakdown->registrationAmbiguous ? 1 : 0);
+ } else {
+ // 未运行真实求解器或评价无效时保持列数一致,诊断字段写空值。
+ int diagnosticColumnCount =
+ isSurrogateScreeningEnabled() ? 9 : 13;
+ for(int i = 0; i < diagnosticColumnCount; ++i) {
+ cols << QString();
+ }
+ }
QTextStream out(&m_traceFile);
out << cols.join(",") << "\n";
@@ -1257,9 +1392,11 @@ void nmCalculationAutoFitPSO::writeIterationTraceRows()
particle.lastEvaluationSuccess,
particle.lastEvaluationElapsedMs,
particle.surrogateObjective,
- particle.screeningDecision,
- particle.bestPosition,
- particle.bestFitness);
+ particle.screeningDecision,
+ particle.bestPosition,
+ particle.bestFitness,
+ particle.evaluatedThisIteration
+ ? &particle.currentObjectiveBreakdown : nullptr);
}
}
@@ -2789,14 +2926,14 @@ void nmCalculationAutoFitPSO::loadParameterBounds()
// 读取用户勾选的拟合参数及上下界。
//
// 这里构建三个核心数组:
- // - m_parameterSelected[8]:完整参数体系中每个参数是否参与拟合;
- // - m_parameterLower/Upper[8]:完整参数体系的搜索上下界;
+ // - m_parameterSelected[10]:完整参数体系中每个参数是否参与拟合;
+ // - m_parameterLower/Upper[10]:完整参数体系的搜索上下界;
// - m_enabledParamIndices:把粒子内部紧凑向量映射回完整参数索引。
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
nmDataAutomaticFitting fittingData = dataManager->getAutomaticFittingDataCopy();
// 获取参数选择状态
- m_parameterSelected.resize(8);
+ m_parameterSelected.resize(10);
m_parameterSelected[0] = fittingData.getPermeabilitySelected();
m_parameterSelected[1] = fittingData.getSkinSelected();
m_parameterSelected[2] = fittingData.getWellboreStorageSelected();
@@ -2805,10 +2942,12 @@ void nmCalculationAutoFitPSO::loadParameterBounds()
m_parameterSelected[5] = fittingData.getCtSelected();
m_parameterSelected[6] = fittingData.getCfSelected();
m_parameterSelected[7] = fittingData.getSwiSelected();
+ m_parameterSelected[8] = fittingData.getFractureConductivitySelected();
+ m_parameterSelected[9] = fittingData.getFractureHalfLengthSelected();
// 获取参数边界
- m_parameterLower.resize(8);
- m_parameterUpper.resize(8);
+ m_parameterLower.resize(10);
+ m_parameterUpper.resize(10);
m_parameterLower[0] = fittingData.getPermeabilityMin().getValue().toDouble();
m_parameterUpper[0] = fittingData.getPermeabilityMax().getValue().toDouble();
@@ -2834,6 +2973,12 @@ void nmCalculationAutoFitPSO::loadParameterBounds()
m_parameterLower[7] = fittingData.getSwiMin().getValue().toDouble();
m_parameterUpper[7] = fittingData.getSwiMax().getValue().toDouble();
+ m_parameterLower[8] = fittingData.getFractureConductivityMin().getValue().toDouble();
+ m_parameterUpper[8] = fittingData.getFractureConductivityMax().getValue().toDouble();
+
+ m_parameterLower[9] = fittingData.getFractureHalfLengthMin().getValue().toDouble();
+ m_parameterUpper[9] = fittingData.getFractureHalfLengthMax().getValue().toDouble();
+
// 更新启用参数索引
m_enabledParamIndices.clear();
@@ -2847,21 +2992,19 @@ void nmCalculationAutoFitPSO::loadParameterBounds()
.arg(m_enabledParamIndices.size()));
}
-// ==================== PSO算法核心方法 ====================
+// ==================== 自动拟合核心方法 ====================
bool nmCalculationAutoFitPSO::startAutoFitting()
{
- // 自动拟合的总入口。可以把这个函数当成 PSO 的“运行剧本”:
- // 读取配置 -> 校验输入 -> 评价用户初始解 -> 初始化粒子群 ->
- // 按代循环评价粒子 -> 更新全局最优 -> 判断停止 -> 保存结果。
+ // 自动拟合总入口:代理开启时保留原 PSO 筛选流程;代理关闭时改走
+ // 诊断灵敏度信赖域搜索。两条路径共用初始解评价、真实求解器和结果写回。
+ StopReasonPSO finalReason = PSO_CONTINUE_OPTIMIZATION;
+ bool useParticleSwarm = false;
+
if(m_isRunning) {
m_lastError = "Auto fitting is already running";
return false;
}
- // 发送初始化日志
- //emit logMessageGenerated(tr("=== PSO Automatic Fitting Started ==="));
- emit logMessageGenerated(tr("Algorithm: Particle Swarm Optimization"));
-
try {
// 从 DataManager 读取界面保存的自动拟合配置。
// 本类不直接依赖 UI 控件,便于后续从脚本或其他入口复用。
@@ -2870,6 +3013,11 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
return false;
}
+ useParticleSwarm = isSurrogateScreeningEnabled();
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("Algorithm: Particle Swarm Optimization")
+ : tr("Algorithm: Diagnostic Trust-Region Search"));
+
if(m_simulationMode) {
// 调试/演示用快速路径,不调用真实求解器。正式工况通常不走这里。
DEBUG_OUT("=== SIMULATION MODE ACTIVATED ===");
@@ -2903,6 +3051,15 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
emit logMessageGenerated(tr("Target data validation passed (%1 data points)").arg(m_targetLogLogData[0].size()));
+ if(m_targetWellName.isEmpty()) {
+ m_lastError = "Target well name is empty";
+ emit logMessageGenerated(tr("ERROR: Target well name is empty"));
+ return false;
+ }
+
+ emit logMessageGenerated(tr("Particle evaluation mode: solve all wells, retain target well '%1' only")
+ .arg(m_targetWellName));
+
// 使用保存的初始值进行精英保护。resetOptimizer() 会清空部分运行状态,
// 所以先把用户当前模型参数缓存下来,后面再恢复用于初始解评价和粒子初始化。
QVector savedInitialValues = m_initialValues;
@@ -2990,6 +3147,8 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
m_globalBestPosition = m_userInitialSolution;
m_userInitialLogLogData = m_lastEvaluatedLogLogData;
m_globalBestLogLogData = m_userInitialLogLogData;
+ m_userInitialObjectiveBreakdown = m_lastObjectiveBreakdown;
+ m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
emit logMessageGenerated(tr("Initial solution evaluation successful"));
emit logMessageGenerated(tr("Initial Error: %1").arg(m_userInitialFitness, 0, 'e', 4));
@@ -3008,9 +3167,11 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
m_userInitialFitness < 1e9,
initialEvalElapsedMs,
std::numeric_limits::quiet_NaN(),
- "initial_solution",
- m_hasValidUserSolution ? m_userInitialSolution : QVector(),
- m_userInitialFitness);
+ "initial_solution",
+ m_hasValidUserSolution ? m_userInitialSolution : QVector(),
+ m_userInitialFitness,
+ m_hasValidUserSolution
+ ? &m_userInitialObjectiveBreakdown : nullptr);
} catch(...) {
m_hasValidUserSolution = false;
emit logMessageGenerated(tr("Exception during initial solution evaluation"));
@@ -3020,12 +3181,13 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
m_initialValues = savedInitialValues;
}
- // 初始化粒子群。粒子维度等于用户勾选的参数数量,而不是固定 11 维。
- if(kUseFixedPsoSeed) {
- emit logMessageGenerated(tr("PSO random seed: %1 ").arg(m_psoRandomSeed));
- } else {
- emit logMessageGenerated(tr("PSO random seed: %1 ").arg(m_psoRandomSeed));
- }
+ if(useParticleSwarm) {
+ // 初始化粒子群。粒子维度等于用户勾选的参数数量,而不是固定 11 维。
+ if(kUseFixedPsoSeed) {
+ emit logMessageGenerated(tr("PSO random seed: %1 ").arg(m_psoRandomSeed));
+ } else {
+ emit logMessageGenerated(tr("PSO random seed: %1 ").arg(m_psoRandomSeed));
+ }
initializeSwarm();
emit logMessageGenerated(tr("Swarm initialized: %1 particles, %2 dimensions").arg(m_swarmSize).arg(getEnabledParameterCount()));
@@ -3174,9 +3336,7 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
.arg(currentSuccessRate * 100, 0, 'f', 1).arg(m_currentIteration + 1));
}
- // 更新全局最优。updateGlobalBest() 只读取粒子的真实 bestFitness,
- // 不使用代理模型的 surrogateObjective。
- //double previousGlobalBest = m_globalBestFitness;
+ // 只从真实求解器确认的粒子 bestFitness 更新全局最优,不使用代理分数。
updateGlobalBest();
// 记录本代所有粒子的真实/代理误差和筛选决策,用于复盘和排障。
writeIterationTraceRows();
@@ -3265,11 +3425,40 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
}
}
+ finalReason = analyzeOptimizationStatus();
+ } else {
+ // 非代理路径不初始化粒子,也不使用 pbest/gbest 速度更新。
+ finalReason = runTrustRegionFitting();
+ }
+
// 最终结果验证和保护
validateAndProtectFinalResult();
+ if(!useParticleSwarm && !m_globalBestPosition.isEmpty() &&
+ m_globalBestObjectiveBreakdown.valid) {
+ // 精英保护可能恢复用户初始解,最终行必须在保护之后写入,确保 trace
+ // 中最后记录的就是实际回写 DataManager 的参数,而非最后一次接受候选。
+ writeTraceRow(m_currentIteration, -1,
+ "trust_region_final",
+ m_globalBestPosition,
+ m_globalBestFitness,
+ m_globalBestFitness < 1.0e9,
+ -1,
+ std::numeric_limits::quiet_NaN(),
+ "final_result",
+ m_globalBestPosition,
+ m_globalBestFitness,
+ &m_globalBestObjectiveBreakdown);
+ }
+
+ if(m_globalBestFitness < m_targetError) {
+ finalReason = PSO_TARGET_ACHIEVED;
+ }
+
} catch(const std::exception& e) {
- m_lastError = QString(tr("Critical exception in PSO main loop: %1")).arg(e.what());
+ m_lastError = useParticleSwarm
+ ? QString(tr("Critical exception in PSO main loop: %1")).arg(e.what())
+ : QString(tr("Critical exception in automatic fitting: %1")).arg(e.what());
emit logMessageGenerated(tr("CRITICAL ERROR: %1").arg(e.what()));
closeTraceFile();
cleanupTemporaryDirectory();
@@ -3277,7 +3466,9 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
emit fittingFinished(false, m_lastError);
return false;
} catch(...) {
- m_lastError = QString(tr("Unknown critical exception in PSO main loop"));
+ m_lastError = useParticleSwarm
+ ? QString(tr("Unknown critical exception in PSO main loop"))
+ : QString(tr("Unknown critical exception in automatic fitting"));
emit logMessageGenerated(tr("CRITICAL ERROR: Unknown exception in PSO main loop"));
closeTraceFile();
cleanupTemporaryDirectory();
@@ -3286,13 +3477,49 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
return false;
}
- m_isRunning = false;
+ bool finalFullSolverSucceeded = true;
+ bool finalFullSolverExecuted = false;
// 应用最终参数
if(!m_globalBestPosition.isEmpty()) {
try {
emit logMessageGenerated(tr("Applying optimized parameters to model..."));
applyParametersToDataManager(m_globalBestPosition);
+
+ // 即使用户此时停止、不再执行最终完整计算,也要把 PEBI 缓存恢复为
+ // 最终已接受的裂缝参数,避免缓存仍停留在最后一个被拒绝的候选值。
+ const bool fractureGridParameterSelected =
+ (m_parameterSelected.size() > 8 && m_parameterSelected[8]) ||
+ (m_parameterSelected.size() > 9 && m_parameterSelected[9]);
+ if(fractureGridParameterSelected) {
+ nmCalculationPebiGrid* pebiGrid = nmCalculationPebiGrid::getInstance();
+ if(!pebiGrid || !pebiGrid->generateOutputPara()) {
+ throw std::runtime_error("Failed to refresh final fracture parameters");
+ }
+ }
+
+ if(m_shouldStop) {
+ // 手动停止优先保持快速返回,仅写回已确认的最优参数。
+ emit logMessageGenerated(tr("Final full-field calculation skipped after user stop"));
+ } else {
+ // 粒子阶段只保留目标井临时曲线。正常结束后用最优参数完整计算一次,
+ // 将全部井曲线和网格压力场写回项目,该次不计入 PSO 粒子评价数。
+ emit logMessageGenerated(tr("Running final full-field calculation with optimized parameters..."));
+ finalFullSolverExecuted = true;
+ finalFullSolverSucceeded = runFinalFullSolver();
+
+ if(finalFullSolverSucceeded) {
+ emit logMessageGenerated(tr("Final full-field calculation completed successfully"));
+ } else if(m_shouldStop) {
+ finalFullSolverExecuted = false;
+ finalFullSolverSucceeded = true;
+ emit logMessageGenerated(tr("Final full-field calculation stopped by user"));
+ } else {
+ emit logMessageGenerated(tr("ERROR: Final full-field calculation failed"));
+ m_lastError = tr("Optimized parameters were found, but the final full-field calculation failed");
+ }
+ }
+
saveOptimizationResult();
// 输出最终优化结果
@@ -3311,56 +3538,94 @@ bool nmCalculationAutoFitPSO::startAutoFitting()
emit logMessageGenerated(finalParams);
- emit logMessageGenerated(tr("Parameters applied successfully to data manager"));
+ if(finalFullSolverExecuted && finalFullSolverSucceeded) {
+ emit logMessageGenerated(tr("Parameters and full-field results applied successfully to data manager"));
+ } else if(!finalFullSolverExecuted) {
+ emit logMessageGenerated(tr("Optimized parameters applied to data manager"));
+ }
} catch(const std::exception& e) {
+ finalFullSolverSucceeded = false;
emit logMessageGenerated(tr("ERROR: Failed to apply final parameters: %1").arg(e.what()));
m_lastError = QString("Failed to apply final parameters: %1").arg(e.what());
} catch(...) {
+ finalFullSolverSucceeded = false;
emit logMessageGenerated(tr("ERROR: Unknown error applying final parameters"));
m_lastError = "Failed to apply final parameters due to unknown error";
}
}
+ m_isRunning = false;
+
// 判断系统确定最终结果
bool success;
QString message;
- StopReasonPSO finalReason = analyzeOptimizationStatus();
if(finalReason == PSO_TARGET_ACHIEVED) {
success = true;
message = QString(tr("Target achieved. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
- emit logMessageGenerated(tr("=== PSO OPTIMIZATION SUCCESSFUL ==="));
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("=== PSO OPTIMIZATION SUCCESSFUL ===")
+ : tr("=== AUTOMATIC FITTING SUCCESSFUL ==="));
} else if(finalReason == PSO_TRUE_CONVERGENCE) {
success = true;
- message = QString(tr("PSO optimization converged to stable solution. Best error: %1, Iterations: %2"))
- .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
- emit logMessageGenerated(tr("=== PSO OPTIMIZATION CONVERGED ==="));
+ message = useParticleSwarm
+ ? QString(tr("PSO optimization converged to stable solution. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1)
+ : QString(tr("Automatic fitting converged to a stable solution. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("=== PSO OPTIMIZATION CONVERGED ===")
+ : tr("=== AUTOMATIC FITTING CONVERGED ==="));
} else if(finalReason == PSO_LOCAL_OPTIMUM) {
success = true;
- message = QString(tr("PSO optimization trapped in local optimum. Best error: %1, Iterations: %2"))
- .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
- emit logMessageGenerated(tr("=== PSO OPTIMIZATION - LOCAL OPTIMUM ==="));
+ message = useParticleSwarm
+ ? QString(tr("PSO optimization trapped in local optimum. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1)
+ : QString(tr("Automatic fitting reached a local optimum. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("=== PSO OPTIMIZATION - LOCAL OPTIMUM ===")
+ : tr("=== AUTOMATIC FITTING - LOCAL OPTIMUM ==="));
} else if(finalReason == PSO_MAX_ITERATIONS) {
success = true;
message = QString(tr("Max iterations reached. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
- emit logMessageGenerated(tr("=== PSO OPTIMIZATION - MAX ITERATIONS ==="));
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("=== PSO OPTIMIZATION - MAX ITERATIONS ===")
+ : tr("=== AUTOMATIC FITTING - MAX ITERATIONS ==="));
} else if(finalReason == PSO_USER_STOPPED) {
success = true;
message = QString(tr("Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
- emit logMessageGenerated(tr("=== PSO OPTIMIZATION STOPPED BY USER ==="));
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("=== PSO OPTIMIZATION STOPPED BY USER ===")
+ : tr("=== AUTOMATIC FITTING STOPPED BY USER ==="));
} else if(finalReason == PSO_CONSECUTIVE_FAILURES) {
success = false;
- message = QString(tr("PSO optimization failed due to consecutive failures. Best error: %1, Iterations: %2"))
- .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
- emit logMessageGenerated(tr("=== PSO OPTIMIZATION FAILED ==="));
+ message = useParticleSwarm
+ ? QString(tr("PSO optimization failed due to consecutive failures. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1)
+ : QString(tr("Automatic fitting failed due to consecutive failures. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("=== PSO OPTIMIZATION FAILED ===")
+ : tr("=== AUTOMATIC FITTING FAILED ==="));
} else {
success = false;
- message = QString(tr("PSO optimization ended unexpectedly. Best error: %1, Iterations: %2"))
- .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
- emit logMessageGenerated(tr("=== PSO OPTIMIZATION - UNKNOWN END ==="));
+ message = useParticleSwarm
+ ? QString(tr("PSO optimization ended unexpectedly. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1)
+ : QString(tr("Automatic fitting ended unexpectedly. Best error: %1, Iterations: %2"))
+ .arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
+ emit logMessageGenerated(useParticleSwarm
+ ? tr("=== PSO OPTIMIZATION - UNKNOWN END ===")
+ : tr("=== AUTOMATIC FITTING - UNKNOWN END ==="));
+ }
+
+ if(!finalFullSolverSucceeded) {
+ success = false;
+ message = m_lastError;
}
emitRunSummary(success, finalReason);
@@ -3446,6 +3711,38 @@ void nmCalculationAutoFitPSO::extractUserInitialValues()
case 7: // 初始含水饱和度
initialValue = reservoirData.getSwi().getValue().toDouble();
break;
+
+ case 8: // 裂缝导流能力
+ if(pTargetWell && pTargetWell->getWellType() == NM_WELL_MODEL::Vertical_Fractured_Well) {
+ nmDataVerticalFracturedWell* fracturedWell =
+ dynamic_cast(pTargetWell);
+ if(fracturedWell) {
+ initialValue = fracturedWell->getDfc().getValue().toDouble();
+ }
+ } else if(pTargetWell && pTargetWell->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) {
+ nmDataHorizontalFracturedWell* fracturedWell =
+ dynamic_cast(pTargetWell);
+ if(fracturedWell) {
+ initialValue = fracturedWell->getDfc().getValue().toDouble();
+ }
+ }
+ break;
+
+ case 9: // 裂缝半长
+ if(pTargetWell && pTargetWell->getWellType() == NM_WELL_MODEL::Vertical_Fractured_Well) {
+ nmDataVerticalFracturedWell* fracturedWell =
+ dynamic_cast(pTargetWell);
+ if(fracturedWell) {
+ initialValue = fracturedWell->getFractureHalfLength().getValue().toDouble();
+ }
+ } else if(pTargetWell && pTargetWell->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) {
+ nmDataHorizontalFracturedWell* fracturedWell =
+ dynamic_cast(pTargetWell);
+ if(fracturedWell) {
+ initialValue = fracturedWell->getFractureHalfLength().getValue().toDouble();
+ }
+ }
+ break;
}
m_initialValues.append(initialValue);
@@ -3490,6 +3787,8 @@ void nmCalculationAutoFitPSO::initializeSwarm()
particle.velocity.resize(dimensions);
particle.bestPosition.resize(dimensions);
particle.guideBestPosition.resize(dimensions);
+ particle.currentObjectiveBreakdown = AutoFitObjectiveBreakdown();
+ particle.bestObjectiveBreakdown = AutoFitObjectiveBreakdown();
particle.bestFitness = 1e10;
particle.guideBestObjective = 1e10;
particle.guideBestFromSurrogate = false;
@@ -3555,162 +3854,1451 @@ void nmCalculationAutoFitPSO::initializeSwarm()
particle.bestPosition = particle.position;
particle.guideBestPosition = particle.position;
+
+ // 第一个粒子可能直接复用用户初始解,因此同步保存初始解的误差分解,
+ // 后续误差引导只能使用真实求解器确认过的 breakdown。
+ if(i == 0 && m_hasValidUserSolution) {
+ particle.currentObjectiveBreakdown = m_userInitialObjectiveBreakdown;
+ particle.bestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
+ }
}
}
-void nmCalculationAutoFitPSO::updateParticle(int particleIndex)
+// 信赖域搜索统一在 [0, 1] 内部坐标工作。正值参数使用对数坐标,使内部相同步长
+// 表示近似相同的相对变化,避免 k、C、Ct、Cf 等跨数量级参数被线性尺度支配;
+// skin 可为负数、Swi 的物理意义是线性比例,因此二者保持有界线性坐标。
+static bool useTrustRegionLogScale(int parameterIndex, double lower, double upper)
{
- if(particleIndex < 0 || particleIndex >= m_swarm.size()) return;
-
- AutoFitParticle& particle = m_swarm[particleIndex];
+ return parameterIndex != 1 && parameterIndex != 7 &&
+ lower > 0.0 && upper > lower;
+}
- // 单粒子真实评价入口。
- // 这里调用 evaluateFitness(),因此会真实写 DataManager、调用求解器、计算误差。
- // 被代理模型筛掉的粒子不会进入这个函数。
- particle.evaluatedThisIteration = false;
- particle.lastEvaluationSuccess = false;
- particle.lastEvaluationElapsedMs = -1;
- particle.pbestRelativeImprovementThisIteration = 0.0;
- particle.selectedForSolver = true;
+static double toTrustRegionCoordinate(double value,
+ int parameterIndex,
+ double lower,
+ double upper)
+{
+ // 所有进入优化器的物理值先投影到用户上下界,再转换成无量纲坐标。
+ // 这样有限差分步长、信赖半径和参数间相关性可以在统一尺度上比较。
+ value = qMax(lower, qMin(upper, value));
- if(particle.screeningDecision.isEmpty()) {
- particle.screeningDecision = "full_solver";
+ if(useTrustRegionLogScale(parameterIndex, lower, upper)) {
+ return (qLn(value) - qLn(lower)) / (qLn(upper) - qLn(lower));
}
- bool reuseInitialSolution = m_currentIteration == 0 &&
- particleIndex == 0 &&
- m_hasValidUserSolution &&
- m_userInitialFitness < 1e9 &&
- particle.position.size() == m_userInitialSolution.size();
-
- for(int i = 0; reuseInitialSolution && i < particle.position.size(); ++i) {
- double tolerance = qMax(1.0e-12, qAbs(m_userInitialSolution[i]) * 1.0e-12);
- reuseInitialSolution = qAbs(particle.position[i] - m_userInitialSolution[i]) <= tolerance;
- }
+ return upper > lower ? (value - lower) / (upper - lower) : 0.0;
+}
- if(reuseInitialSolution) {
- // 初始解在进入粒子群前已经真实求解过。第一代第0号粒子位置完全相同,
- // 直接复用真实误差和曲线,避免一次重复 DLL 调用且不改变 PSO 数学状态。
- particle.fitness = m_userInitialFitness;
- particle.currentLogLogData = m_userInitialLogLogData;
- particle.lastEvaluationElapsedMs = 0;
- particle.evaluatedThisIteration = true;
- particle.lastEvaluationSuccess = true;
- particle.screeningDecision = "initial_solution_cache";
- emit logMessageGenerated(tr("Particle 1 reused the verified initial solution"));
- } else {
- QTime evalTimer;
- evalTimer.start();
- particle.fitness = evaluateFitness(particle.position);
- particle.currentLogLogData = m_lastEvaluatedLogLogData;
- particle.lastEvaluationElapsedMs = evalTimer.elapsed();
- particle.evaluatedThisIteration = true;
- particle.lastEvaluationSuccess = (particle.fitness < 1e9);
- m_totalEvaluations++;
+static double fromTrustRegionCoordinate(double coordinate,
+ int parameterIndex,
+ double lower,
+ double upper)
+{
+ // 候选内部坐标先限制在 [0,1],再执行上述映射的逆变换,保证写回
+ // DataManager 的参数始终位于用户设置的物理范围内。
+ coordinate = qMax(0.0, qMin(1.0, coordinate));
- if(particle.fitness < 1e9) {
- m_successfulEvaluations++;
- }
+ if(useTrustRegionLogScale(parameterIndex, lower, upper)) {
+ return qExp(qLn(lower) + coordinate * (qLn(upper) - qLn(lower)));
}
- // 更新个体最优 pbest。这里使用的是真实求解器误差 particle.fitness,
- // 不是代理模型给出的 surrogateObjective。
- double previousBestFitness = particle.bestFitness;
+ return lower + coordinate * (upper - lower);
+}
- if(particle.fitness < previousBestFitness) {
- particle.pbestRelativeImprovementThisIteration = previousBestFitness >= 1e9
- ? 1.0
- : (previousBestFitness - particle.fitness) /
- qMax(1e-10, qAbs(previousBestFitness));
+enum TrustRegionErrorComponent
+{
+ TRUST_REGION_VERTICAL_COMPONENT = 0,
+ TRUST_REGION_HORIZONTAL_COMPONENT,
+ TRUST_REGION_SHAPE_COMPONENT,
+ TRUST_REGION_TOTAL_COMPONENT
+};
- // 如果当前位置尚未成为新的真实全局最优,而上一代存在尚未真实验证、且代理
- // 仍判断更优的 guide,就保留它继续引导速度;真实 pbest 仍照常更新。
- bool preserveSurrogateGuide = false;
+// 一次真实求解的完整快照。除了参数和总误差,还保存内部坐标、诊断分量和
+// 双对数曲线,因此拒绝候选后可以完整恢复上一个已接受工作点。
+struct TrustRegionEvaluation
+{
+ QVector parameters;
+ QVector coordinates;
+ AutoFitObjectiveBreakdown breakdown;
+ QVector > curve;
+ double fitness;
+ int elapsedMs;
+ bool valid;
+
+ TrustRegionEvaluation()
+ : fitness(1.0e10)
+ , elapsedMs(-1)
+ , valid(false)
+ {}
+};
- bool currentBeatsGlobalBest = particle.fitness < m_globalBestFitness;
+// LM 只使用固定长度、全部有限的普通残差。代理路径不会进入本搜索器。
+static bool trustRegionResidualsValid(
+ const AutoFitObjectiveBreakdown& breakdown)
+{
+ // 损失函数固定使用 80 个压力点和 80 个导数点。严格校验长度,避免
+ // Jacobian 沿用旧维度后访问另一候选的短残差向量。
+ if(!breakdown.valid || breakdown.residualVector.size() != 160) {
+ return false;
+ }
- if(!currentBeatsGlobalBest &&
- isSurrogateScreeningEnabled() &&
- particle.guideBestFromSurrogate &&
- isFiniteNumber(particle.guideBestObjective) &&
- isFiniteNumber(particle.surrogateObjective)) {
- double requiredImprovement = qMax(1.0e-10,
- qAbs(particle.guideBestObjective) *
- kSurrogateGuidePbestMinRelativeImprovement);
- preserveSurrogateGuide = particle.surrogateObjective -
- particle.guideBestObjective > requiredImprovement;
+ for(int i = 0; i < breakdown.residualVector.size(); ++i) {
+ if(!isFiniteNumber(breakdown.residualVector[i])) {
+ return false;
}
+ }
+ return true;
+}
- particle.bestFitness = particle.fitness;
- particle.bestPosition = particle.position;
- particle.bestLogLogData = particle.currentLogLogData;
+// 计算向量二范数的平方,避免在只比较能量或计算正规方程时反复开方。
+static double trustRegionSquaredNorm(const QVector& values)
+{
+ double sum = 0.0;
+ for(int i = 0; i < values.size(); ++i) {
+ sum += values[i] * values[i];
+ }
+ return sum;
+}
- if(!preserveSurrogateGuide) {
- particle.guideBestPosition = particle.position;
- particle.guideBestObjective = isFiniteNumber(particle.surrogateObjective)
- ? particle.surrogateObjective
- : particle.fitness;
- particle.guideBestFromSurrogate = false;
- }
+// 计算同维向量内积;维度不一致表示局部模型无效,返回零让调用方放弃修正。
+static double trustRegionDotProduct(const QVector& left,
+ const QVector& right)
+{
+ if(left.size() != right.size()) {
+ return 0.0;
+ }
- DEBUG_OUT(QString("Particle %1 improved: error = %2")
- .arg(particleIndex).arg(particle.fitness, 0, 'e', 4));
+ double sum = 0.0;
+ for(int i = 0; i < left.size(); ++i) {
+ sum += left[i] * right[i];
}
+ return sum;
}
-void nmCalculationAutoFitPSO::updateGlobalBest()
+// trace 和运行日志使用稳定的英文标识,便于现有离线脚本继续按字段筛选。
+static QString trustRegionComponentName(int component)
{
- // 保存上一轮的全局最优,用于后续自适应参数调整等
- m_previousBestFitness = m_globalBestFitness;
- bool globalBestUpdated = false;
+ if(component == TRUST_REGION_VERTICAL_COMPONENT) {
+ return "vertical";
+ }
+ if(component == TRUST_REGION_HORIZONTAL_COMPONENT) {
+ return "horizontal";
+ }
+ if(component == TRUST_REGION_SHAPE_COMPONENT) {
+ return "shape";
+ }
+ return "total";
+}
- // 遍历所有粒子,寻找比当前 global best 更好的个体最优。
- // 注意:particle.bestFitness 只有在真实求解器评价成功后才会更新。
- for(int i = 0; i < m_swarm.size(); ++i) {
- const AutoFitParticle& particle = m_swarm[i];
+// 三类损失量纲一致,直接选择当前最大的可靠分量;都很小时退回总残差梯度。
+static int trustRegionDominantComponent(
+ const AutoFitObjectiveBreakdown& breakdown,
+ double diagnosisThreshold)
+{
+ int component = TRUST_REGION_TOTAL_COMPONENT;
+ double largestLoss = diagnosisThreshold;
- // 只要个体最优比当前全局最优小,就认为是更好的解
- if(particle.bestFitness < m_globalBestFitness) {
+ if(breakdown.verticalReliable &&
+ isFiniteNumber(breakdown.verticalLoss) &&
+ breakdown.verticalLoss > largestLoss) {
+ component = TRUST_REGION_VERTICAL_COMPONENT;
+ largestLoss = breakdown.verticalLoss;
+ }
+ if(breakdown.horizontalReliable &&
+ !breakdown.registrationAmbiguous &&
+ isFiniteNumber(breakdown.horizontalLoss) &&
+ breakdown.horizontalLoss > largestLoss) {
+ component = TRUST_REGION_HORIZONTAL_COMPONENT;
+ largestLoss = breakdown.horizontalLoss;
+ }
+ if(isFiniteNumber(breakdown.shapeLoss) &&
+ breakdown.shapeLoss > largestLoss) {
+ component = TRUST_REGION_SHAPE_COMPONENT;
+ }
- double improvement = m_globalBestFitness - particle.bestFitness;
- double relativeImprovement =
- improvement / qMax(1e-10, qAbs(m_globalBestFitness));
+ return component;
+}
- // 区分显著改进和微小改进,但无论如何都会更新全局最优
- if(relativeImprovement > m_improvementThreshold) {
- DEBUG_OUT(QString(tr("Global best updated with %1% improvement: %2"))
- .arg(relativeImprovement * 100.0, 0, 'f', 3)
- .arg(particle.bestFitness, 0, 'e', 4));
- } else {
- DEBUG_OUT(QString(tr("Global best updated (minor improvement %1% < %2%) to %3"))
- .arg(relativeImprovement * 100.0, 0, 'f', 3)
- .arg(m_improvementThreshold * 100.0, 0, 'f', 2)
- .arg(particle.bestFitness, 0, 'e', 4));
- }
+// 求解选中参数对应的阻尼正规方程。上下和左右诊断量保留方向;形状没有
+// 天然正负,因此使用 shapeLoss 对参数的局部导数。参数最多八维,使用带
+// 部分主元的高斯消元即可处理该小矩阵,并在主元退化时明确返回失败。
+static bool solveTrustRegionLinearSystem(
+ QVector > matrix,
+ QVector rightHandSide,
+ QVector* solution)
+{
+ if(!solution || matrix.isEmpty() ||
+ matrix.size() != rightHandSide.size()) {
+ return false;
+ }
- // 无论相对改进是否超过阈值,都要更新全局最优
- m_globalBestFitness = particle.bestFitness;
- m_globalBestPosition = particle.bestPosition;
- m_globalBestLogLogData = particle.bestLogLogData;
- globalBestUpdated = true;
- emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
+ const int size = matrix.size();
+ for(int i = 0; i < size; ++i) {
+ if(matrix[i].size() != size) {
+ return false;
}
}
- // 只有在本轮没有找到任何更好的解时才考虑恢复用户初始解
- if(!globalBestUpdated && m_hasValidUserSolution && m_userInitialFitness < m_globalBestFitness) {
-
- double improvement = m_globalBestFitness - m_userInitialFitness;
- double relativeImprovement =
- improvement / qMax(1e-10, qAbs(m_globalBestFitness));
+ for(int column = 0; column < size; ++column) {
+ int pivotRow = column;
+ double pivotMagnitude = qAbs(matrix[column][column]);
+ for(int row = column + 1; row < size; ++row) {
+ double magnitude = qAbs(matrix[row][column]);
+ if(magnitude > pivotMagnitude) {
+ pivotMagnitude = magnitude;
+ pivotRow = row;
+ }
+ }
+ if(pivotMagnitude <= 1.0e-14) {
+ return false;
+ }
- DEBUG_OUT("Elite protection: Restoring user initial solution as global best");
- DEBUG_OUT(QString("Elite solution is better than current best by %1%")
- .arg(relativeImprovement * 100.0, 0, 'f', 3));
+ if(pivotRow != column) {
+ qSwap(matrix[pivotRow], matrix[column]);
+ qSwap(rightHandSide[pivotRow], rightHandSide[column]);
+ }
- m_globalBestFitness = m_userInitialFitness;
+ for(int row = column + 1; row < size; ++row) {
+ double factor = matrix[row][column] /
+ matrix[column][column];
+ matrix[row][column] = 0.0;
+ for(int nextColumn = column + 1;
+ nextColumn < size; ++nextColumn) {
+ matrix[row][nextColumn] -=
+ factor * matrix[column][nextColumn];
+ }
+ rightHandSide[row] -= factor * rightHandSide[column];
+ }
+ }
+
+ solution->fill(0.0, size);
+ for(int row = size - 1; row >= 0; --row) {
+ double value = rightHandSide[row];
+ for(int column = row + 1; column < size; ++column) {
+ value -= matrix[row][column] * (*solution)[column];
+ }
+ double pivot = matrix[row][row];
+ if(qAbs(pivot) <= 1.0e-14) {
+ return false;
+ }
+ (*solution)[row] = value / pivot;
+ if(!isFiniteNumber((*solution)[row])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// 计算两个 Jacobian 列向量的绝对余弦相似度。接近 1 表示两个参数在当前
+// 工作点对曲线的影响几乎相同,联合调整容易产生不可辨识方向。
+static double trustRegionJacobianColumnCorrelation(
+ const QVector >& jacobian,
+ int leftColumn,
+ int rightColumn)
+{
+ double product = 0.0;
+ double leftNorm = 0.0;
+ double rightNorm = 0.0;
+ for(int row = 0; row < jacobian.size(); ++row) {
+ if(leftColumn >= jacobian[row].size() ||
+ rightColumn >= jacobian[row].size()) {
+ return 1.0;
+ }
+ double left = jacobian[row][leftColumn];
+ double right = jacobian[row][rightColumn];
+ product += left * right;
+ leftNorm += left * left;
+ rightNorm += right * right;
+ }
+
+ if(leftNorm <= 1.0e-20 || rightNorm <= 1.0e-20) {
+ return 0.0;
+ }
+ return qAbs(product) / qSqrt(leftNorm * rightNorm);
+}
+
+// 每次接受一个真实候选后,使用满足最新割线条件的秩一修正更新完整残差
+// Jacobian。这样模型吸收了刚得到的真实变化,又不必立即逐参数重新试算。
+static void updateTrustRegionJacobian(
+ QVector >* jacobian,
+ const QVector& oldResidual,
+ const QVector& newResidual,
+ const QVector& coordinateStep)
+{
+ if(!jacobian || jacobian->size() != oldResidual.size() ||
+ oldResidual.size() != newResidual.size()) {
+ return;
+ }
+
+ double denominator = trustRegionSquaredNorm(coordinateStep);
+ if(denominator <= 1.0e-12) {
+ return;
+ }
+
+ for(int row = 0; row < jacobian->size(); ++row) {
+ if((*jacobian)[row].size() != coordinateStep.size()) {
+ return;
+ }
+
+ double predictedChange = 0.0;
+ for(int column = 0; column < coordinateStep.size(); ++column) {
+ predictedChange +=
+ (*jacobian)[row][column] * coordinateStep[column];
+ }
+ double correction =
+ (newResidual[row] - oldResidual[row] - predictedChange) /
+ denominator;
+ for(int column = 0; column < coordinateStep.size(); ++column) {
+ (*jacobian)[row][column] +=
+ correction * coordinateStep[column];
+ }
+ }
+}
+
+// 对上下偏差、左右偏差和形状损失的梯度执行同样的割线秩一修正,使诊断
+// 选参模型与完整残差 Jacobian 保持在同一个已接受工作点。
+static void updateTrustRegionScalarGradient(
+ QVector* gradient,
+ double oldValue,
+ double newValue,
+ const QVector& coordinateStep)
+{
+ if(!gradient || gradient->size() != coordinateStep.size() ||
+ !isFiniteNumber(oldValue) || !isFiniteNumber(newValue)) {
+ return;
+ }
+
+ double denominator = trustRegionSquaredNorm(coordinateStep);
+ if(denominator <= 1.0e-12) {
+ return;
+ }
+
+ double predictedChange = trustRegionDotProduct(
+ *gradient, coordinateStep);
+ double correction =
+ (newValue - oldValue - predictedChange) / denominator;
+ for(int i = 0; i < gradient->size(); ++i) {
+ (*gradient)[i] += correction * coordinateStep[i];
+ }
+}
+
+bool nmCalculationAutoFitPSO::evaluateTrustRegionPoint(
+ const QVector& parameters,
+ double* fitness,
+ AutoFitObjectiveBreakdown* breakdown,
+ QVector >* curve,
+ int* elapsedMs)
+{
+ if(!fitness || !breakdown || !curve || !elapsedMs || m_shouldStop) {
+ return false;
+ }
+
+ // evaluateFitness() 会写入 DataManager 并调用真实求解器。这里统一统计
+ // 真实评价次数和耗时,同时严格要求固定残差、诊断结构和结果曲线均有效。
+ QTime timer;
+ timer.start();
+ *fitness = evaluateFitness(parameters);
+ *elapsedMs = timer.elapsed();
+ *breakdown = m_lastObjectiveBreakdown;
+ *curve = m_lastEvaluatedLogLogData;
+ ++m_totalEvaluations;
+
+ bool valid = isFiniteNumber(*fitness) && *fitness < 1.0e9 &&
+ breakdown->valid &&
+ trustRegionResidualsValid(*breakdown) &&
+ !curve->isEmpty();
+ if(valid) {
+ ++m_successfulEvaluations;
+ }
+
+ return valid;
+}
+
+StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting()
+{
+ const int dimensions = getEnabledParameterCount();
+ if(dimensions <= 0 || m_enabledParamIndices.size() != dimensions) {
+ m_lastError = tr("No valid parameters are available for trust-region fitting");
+ return PSO_OPTIMIZATION_FAILED;
+ }
+
+ // 真实求解次数比“外层迭代次数”更能反映耗时。预算至少允许完成一次全参数
+ // 灵敏度和两次候选评价,同时避免连续重建 Jacobian 导致运行时间失控。
+ const int maximumEvaluations = qMax(
+ m_totalEvaluations + dimensions + 2,
+ qMax(20, m_maxIterations * 3));
+ // 下列步长均位于归一化内部坐标:0.04 表示参数范围的 4%,信赖半径
+ // 限制一次联合移动的二范数,相关性门槛用于排除响应近乎共线的参数。
+ const double sensitivityStep = 0.04;
+ const double minimumCoordinateStep = 1.0e-5;
+ const double minimumTrustRadius = 2.0e-3;
+ const double maximumTrustRadius = 0.30;
+ const double columnCorrelationLimit = 0.995;
+ const double diagnosisThreshold = 1.0e-5;
+ // 误差下降至少达到绝对 1e-5 且相对当前有效基准 0.2% 才算有效改善。
+ // 更小的下降仍保留为最佳解,但不能反复清除停滞状态、延长拟合时间。
+ const double effectiveRelativeImprovement = 2.0e-3;
+ const double effectiveAbsoluteImprovement = 1.0e-5;
+ const int maximumIneffectiveSteps = 3;
+
+ // damping 是 LM 阻尼;拒绝或预测失准时增大,真实下降与预测一致时减小。
+ // 两组累计量控制 Jacobian 重建,避免长期使用已偏离当前工作点的局部模型。
+ double trustRadius = 0.12;
+ double damping = 1.0e-2;
+ int consecutiveRejectedSteps = 0;
+ int consecutiveSolverFailures = 0;
+ int acceptedSinceRebuild = 0;
+ int consecutiveIneffectiveSteps = 0;
+ double movementSinceRebuild = 0.0;
+ bool rebuildRequested = true;
+ bool modelRebuiltAtMinimumRadius = false;
+ bool stagnationConfirmationRequested = false;
+ StopReasonPSO stopReason = PSO_MAX_ITERATIONS;
+
+ // jacobian 的行对应固定 160 维残差,列对应用户勾选的参数。
+ // 三个 gradient 单独描述诊断分量对参数的局部变化,只用于本轮选参。
+ QVector > jacobian;
+ QVector verticalGradient(dimensions, 0.0);
+ QVector horizontalGradient(dimensions, 0.0);
+ QVector shapeGradient(dimensions, 0.0);
+ QVector jacobianColumnValid(dimensions, false);
+
+ // 参数向量的顺序始终与 m_enabledParamIndices 一致,不能按完整参数索引
+ // 直接访问;下面两个转换函数集中维护这层映射关系。
+ auto coordinatesFromParameters = [&](const QVector& parameters)
+ -> QVector {
+ QVector coordinates(dimensions, 0.0);
+ for(int i = 0; i < dimensions; ++i) {
+ int parameterIndex = m_enabledParamIndices[i];
+ coordinates[i] = toTrustRegionCoordinate(
+ parameters[i], parameterIndex,
+ m_parameterLower[parameterIndex],
+ m_parameterUpper[parameterIndex]);
+ }
+ return coordinates;
+ };
+
+ auto parametersFromCoordinates = [&](const QVector& coordinates)
+ -> QVector {
+ QVector parameters(dimensions, 0.0);
+ for(int i = 0; i < dimensions; ++i) {
+ int parameterIndex = m_enabledParamIndices[i];
+ parameters[i] = fromTrustRegionCoordinate(
+ coordinates[i], parameterIndex,
+ m_parameterLower[parameterIndex],
+ m_parameterUpper[parameterIndex]);
+ }
+ return parameters;
+ };
+
+ auto restoreEvaluationState = [&](const TrustRegionEvaluation& evaluation) {
+ // evaluateFitness() 会把试算参数写入 DataManager。无论候选是否接受,
+ // 下一次计算前都恢复到唯一的已接受工作点,防止失败试算污染后续求解。
+ applyParametersToDataManager(evaluation.parameters);
+ m_lastObjectiveBreakdown = evaluation.breakdown;
+ m_lastEvaluatedLogLogData = evaluation.curve;
+ };
+
+ // 只有真实总误差更小的工作点才能发布为全局最优;曲线和诊断快照必须
+ // 与参数同步更新,防止界面显示或最终精英保护使用错配的数据。
+ auto publishAcceptedPoint = [&](const TrustRegionEvaluation& evaluation) {
+ m_previousBestFitness = m_globalBestFitness;
+ m_globalBestPosition = evaluation.parameters;
+ m_globalBestFitness = evaluation.fitness;
+ m_globalBestObjectiveBreakdown = evaluation.breakdown;
+ m_globalBestLogLogData = evaluation.curve;
+ emit bestCurveUpdated(m_targetLogLogData,
+ m_globalBestLogLogData,
+ m_currentIteration + 1,
+ m_globalBestFitness);
+ };
+
+ auto processPauseAndStop = [&]() -> bool {
+ while(m_isPaused && !m_shouldStop) {
+ QApplication::processEvents();
+ msleep(100);
+ }
+ QApplication::processEvents();
+ return !m_shouldStop;
+ };
+
+ // current 始终代表唯一已接受工作点。优先复用启动阶段已经真实验证的
+ // 用户初始解,避免在信赖域入口重复调用一次昂贵求解器。
+ TrustRegionEvaluation current;
+ if(m_hasValidUserSolution &&
+ m_globalBestPosition.size() == dimensions &&
+ trustRegionResidualsValid(m_globalBestObjectiveBreakdown) &&
+ !m_globalBestLogLogData.isEmpty()) {
+ current.parameters = m_globalBestPosition;
+ current.coordinates = coordinatesFromParameters(current.parameters);
+ current.breakdown = m_globalBestObjectiveBreakdown;
+ current.curve = m_globalBestLogLogData;
+ current.fitness = m_globalBestFitness;
+ current.elapsedMs = 0;
+ current.valid = true;
+ } else {
+ // 用户初始解无效时只做一次确定性的范围中点回退;所有正值参数在对数
+ // 坐标取中点,避免线性中点过分偏向跨数量级范围的上界。
+ current.coordinates.fill(0.5, dimensions);
+ current.parameters = parametersFromCoordinates(current.coordinates);
+ current.valid = evaluateTrustRegionPoint(
+ current.parameters,
+ ¤t.fitness,
+ ¤t.breakdown,
+ ¤t.curve,
+ ¤t.elapsedMs);
+ writeTraceRow(-1, -1,
+ "trust_region_midpoint",
+ current.parameters,
+ current.fitness,
+ current.valid,
+ current.elapsedMs,
+ std::numeric_limits::quiet_NaN(),
+ current.valid ? "midpoint_valid" : "midpoint_invalid",
+ QVector(),
+ 1.0e10,
+ current.valid ? ¤t.breakdown : nullptr);
+ if(!current.valid) {
+ m_lastError = tr("The initial solution and parameter-range midpoint are both invalid");
+ return m_shouldStop
+ ? PSO_USER_STOPPED
+ : PSO_OPTIMIZATION_FAILED;
+ }
+ publishAcceptedPoint(current);
+ }
+
+ restoreEvaluationState(current);
+ m_convergenceHistory.append(current.fitness);
+ emit logMessageGenerated(
+ tr("Trust-region initial error: %1; evaluation budget: %2")
+ .arg(current.fitness, 0, 'e', 4)
+ .arg(maximumEvaluations));
+
+ // 有效改善始终相对“上一次有效改善后的误差”累计判断,避免一连串微小
+ // 下降每次都清零计数;累计达到门槛后才开始新的有效改善基准。
+ double effectiveImprovementBaseline = current.fitness;
+ auto registerEffectiveImprovement = [&](double fitness) -> bool {
+ const double requiredImprovement = qMax(
+ effectiveAbsoluteImprovement,
+ qAbs(effectiveImprovementBaseline) *
+ effectiveRelativeImprovement);
+ const double improvement = effectiveImprovementBaseline - fitness;
+ if(improvement < requiredImprovement) {
+ return false;
+ }
+
+ effectiveImprovementBaseline = fitness;
+ consecutiveIneffectiveSteps = 0;
+ stagnationConfirmationRequested = false;
+ return true;
+ };
+
+ // 连续三次没有有效改善时只请求一次灵敏度重建。重建完成后由主循环
+ // 直接检查累计改善,仍达不到门槛就判定局部收敛,不再继续微小试探。
+ auto recordIneffectiveStep = [&]() -> bool {
+ ++consecutiveIneffectiveSteps;
+ if(consecutiveIneffectiveSteps < maximumIneffectiveSteps) {
+ return false;
+ }
+
+ if(stagnationConfirmationRequested) {
+ return true;
+ }
+
+ consecutiveIneffectiveSteps = 0;
+ stagnationConfirmationRequested = true;
+ rebuildRequested = true;
+ emit logMessageGenerated(
+ tr("No effective improvement for %1 consecutive steps; "
+ "rebuilding sensitivity model for confirmation")
+ .arg(maximumIneffectiveSteps));
+ return false;
+ };
+
+ emit logMessageGenerated(
+ tr("Effective improvement threshold: max(%1, %2% of baseline error); "
+ "%3 consecutive ineffective steps trigger convergence confirmation")
+ .arg(effectiveAbsoluteImprovement, 0, 'e', 2)
+ .arg(effectiveRelativeImprovement * 100.0, 0, 'f', 2)
+ .arg(maximumIneffectiveSteps));
+
+ if(current.fitness < m_targetError) {
+ return PSO_TARGET_ACHIEVED;
+ }
+
+ // 在同一个真实工作点逐参数做单边差分。首选可用空间更大的方向;只有该方向
+ // 求解失败时才补算反方向,因此初次建模通常每个参数只增加一次真实求解。
+ auto rebuildSensitivity = [&]() -> bool {
+ const TrustRegionEvaluation base = current;
+ const int residualCount = base.breakdown.residualVector.size();
+ if(residualCount <= 0) {
+ return false;
+ }
+
+ jacobian = QVector >(
+ residualCount, QVector(dimensions, 0.0));
+ verticalGradient.fill(0.0, dimensions);
+ horizontalGradient.fill(0.0, dimensions);
+ shapeGradient.fill(0.0, dimensions);
+ jacobianColumnValid.fill(false, dimensions);
+
+ TrustRegionEvaluation bestProbe;
+ int bestProbeColumn = -1;
+ double bestProbeDelta = 0.0;
+ // 差分步长不超过参数范围的 4%,信赖域收缩后同步减小,但保留 0.5%
+ // 下限,避免步长太小使求解器数值噪声淹没真实灵敏度。
+ const double finiteDifferenceStep = qMin(
+ sensitivityStep,
+ qMax(5.0e-3, trustRadius * 0.5));
+
+ for(int column = 0;
+ column < dimensions &&
+ m_totalEvaluations < maximumEvaluations &&
+ processPauseAndStop();
+ ++column) {
+ // 单边差分优先选择离边界空间更大的方向;首方向求解无效时才反向
+ // 补算,因此正常情况下每个参数只消耗一次真实求解。
+ double positiveRoom = 1.0 - base.coordinates[column];
+ double negativeRoom = base.coordinates[column];
+ double preferredSign = positiveRoom >= negativeRoom ? 1.0 : -1.0;
+ bool columnBuilt = false;
+
+ for(int directionAttempt = 0;
+ directionAttempt < 2 &&
+ !columnBuilt &&
+ m_totalEvaluations < maximumEvaluations;
+ ++directionAttempt) {
+ double direction = directionAttempt == 0
+ ? preferredSign : -preferredSign;
+ double availableRoom = direction > 0.0
+ ? positiveRoom : negativeRoom;
+ double deltaMagnitude = qMin(
+ finiteDifferenceStep, availableRoom);
+ if(deltaMagnitude < minimumCoordinateStep) {
+ continue;
+ }
+
+ TrustRegionEvaluation probe;
+ probe.coordinates = base.coordinates;
+ probe.coordinates[column] += direction * deltaMagnitude;
+ probe.parameters = parametersFromCoordinates(probe.coordinates);
+ probe.valid = evaluateTrustRegionPoint(
+ probe.parameters,
+ &probe.fitness,
+ &probe.breakdown,
+ &probe.curve,
+ &probe.elapsedMs);
+
+ QString decision = probe.valid
+ ? "sensitivity_valid"
+ : (directionAttempt == 0
+ ? "sensitivity_retry_opposite"
+ : "sensitivity_invalid");
+ writeTraceRow(m_currentIteration,
+ column,
+ "trust_region_sensitivity",
+ probe.parameters,
+ probe.fitness,
+ probe.valid,
+ probe.elapsedMs,
+ std::numeric_limits::quiet_NaN(),
+ decision,
+ base.parameters,
+ base.fitness,
+ probe.valid ? &probe.breakdown : nullptr);
+
+ if(!probe.valid) {
+ restoreEvaluationState(base);
+ continue;
+ }
+
+ double delta = probe.coordinates[column] -
+ base.coordinates[column];
+ if(qAbs(delta) < minimumCoordinateStep ||
+ probe.breakdown.residualVector.size() != residualCount) {
+ restoreEvaluationState(base);
+ continue;
+ }
+
+ // 第 column 列是固定残差向量相对内部参数坐标的有限差分:
+ // J[:,column] = (r_probe-r_base)/delta。
+ for(int row = 0; row < residualCount; ++row) {
+ jacobian[row][column] =
+ (probe.breakdown.residualVector[row] -
+ base.breakdown.residualVector[row]) / delta;
+ }
+
+ // 有符号诊断量只有在基点和试算点都可靠时才能计算方向梯度;
+ // shapeLoss 无方向可靠性标志,始终记录其局部变化率。
+ if(base.breakdown.verticalReliable &&
+ probe.breakdown.verticalReliable &&
+ !base.breakdown.registrationAmbiguous &&
+ !probe.breakdown.registrationAmbiguous) {
+ verticalGradient[column] =
+ (probe.breakdown.verticalCommonBias -
+ base.breakdown.verticalCommonBias) / delta;
+ }
+ if(base.breakdown.horizontalReliable &&
+ probe.breakdown.horizontalReliable &&
+ !base.breakdown.registrationAmbiguous &&
+ !probe.breakdown.registrationAmbiguous) {
+ horizontalGradient[column] =
+ (probe.breakdown.horizontalPhysicalShift -
+ base.breakdown.horizontalPhysicalShift) / delta;
+ }
+ shapeGradient[column] =
+ (probe.breakdown.shapeLoss -
+ base.breakdown.shapeLoss) / delta;
+ jacobianColumnValid[column] = true;
+ columnBuilt = true;
+
+ if(probe.fitness < base.fitness &&
+ (!bestProbe.valid ||
+ probe.fitness < bestProbe.fitness)) {
+ bestProbe = probe;
+ bestProbeColumn = column;
+ bestProbeDelta = delta;
+ }
+ restoreEvaluationState(base);
+ }
+ }
+
+ int validColumnCount = 0;
+ for(int i = 0; i < jacobianColumnValid.size(); ++i) {
+ if(jacobianColumnValid[i]) {
+ ++validColumnCount;
+ }
+ }
+ if(validColumnCount == 0 || m_shouldStop) {
+ restoreEvaluationState(base);
+ return false;
+ }
+
+ // 灵敏度试算本身若找到更优真实解也应保留。所有列先基于同一个 base
+ // 建完,再用该已知割线把 Jacobian 平移到新工作点,避免边算边移动基点。
+ if(bestProbe.valid && bestProbeColumn >= 0) {
+ QVector acceptedStep(dimensions, 0.0);
+ acceptedStep[bestProbeColumn] = bestProbeDelta;
+ updateTrustRegionJacobian(
+ &jacobian,
+ base.breakdown.residualVector,
+ bestProbe.breakdown.residualVector,
+ acceptedStep);
+ if(base.breakdown.verticalReliable &&
+ bestProbe.breakdown.verticalReliable) {
+ updateTrustRegionScalarGradient(
+ &verticalGradient,
+ base.breakdown.verticalCommonBias,
+ bestProbe.breakdown.verticalCommonBias,
+ acceptedStep);
+ }
+ if(base.breakdown.horizontalReliable &&
+ bestProbe.breakdown.horizontalReliable) {
+ updateTrustRegionScalarGradient(
+ &horizontalGradient,
+ base.breakdown.horizontalPhysicalShift,
+ bestProbe.breakdown.horizontalPhysicalShift,
+ acceptedStep);
+ }
+ updateTrustRegionScalarGradient(
+ &shapeGradient,
+ base.breakdown.shapeLoss,
+ bestProbe.breakdown.shapeLoss,
+ acceptedStep);
+
+ current = bestProbe;
+ publishAcceptedPoint(current);
+ restoreEvaluationState(current);
+ m_convergenceHistory.append(current.fitness);
+ writeTraceRow(m_currentIteration,
+ bestProbeColumn,
+ "trust_region_sensitivity_accept",
+ current.parameters,
+ current.fitness,
+ true,
+ 0,
+ std::numeric_limits::quiet_NaN(),
+ "accepted_cached_probe",
+ current.parameters,
+ current.fitness,
+ ¤t.breakdown);
+ emit logMessageGenerated(
+ tr("Sensitivity probe accepted: error reduced to %1")
+ .arg(current.fitness, 0, 'e', 4));
+ } else {
+ restoreEvaluationState(current);
+ }
+
+ acceptedSinceRebuild = 0;
+ movementSinceRebuild = 0.0;
+ consecutiveRejectedSteps = 0;
+ rebuildRequested = false;
+ // 若重建过程中接受了试算点,当前模型已通过割线平移而不是在新点完整
+ // 重算;再遇到最小半径停滞时仍允许做一次真正的新点重建。
+ modelRebuiltAtMinimumRadius =
+ trustRadius <= minimumTrustRadius * 1.01 &&
+ !bestProbe.valid;
+ emit logMessageGenerated(
+ tr("Sensitivity model rebuilt: %1/%2 parameter columns valid")
+ .arg(validColumnCount)
+ .arg(dimensions));
+ return true;
+ };
+
+ int completedIterations = 0;
+ for(int iteration = 0;
+ iteration < m_maxIterations &&
+ m_totalEvaluations < maximumEvaluations &&
+ !m_shouldStop;
+ ++iteration) {
+ m_currentIteration = iteration;
+ completedIterations = iteration + 1;
+
+ if(!processPauseAndStop()) {
+ break;
+ }
+ if(rebuildRequested) {
+ const bool confirmingStagnation =
+ stagnationConfirmationRequested;
+ if(!rebuildSensitivity()) {
+ stopReason = m_shouldStop
+ ? PSO_USER_STOPPED
+ : PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ if(current.fitness < m_targetError) {
+ stopReason = PSO_TARGET_ACHIEVED;
+ break;
+ }
+ if(m_totalEvaluations >= maximumEvaluations) {
+ stopReason = PSO_MAX_ITERATIONS;
+ break;
+ }
+ const bool rebuildEffective =
+ registerEffectiveImprovement(current.fitness);
+ if(confirmingStagnation && !rebuildEffective) {
+ emit logMessageGenerated(
+ tr("Sensitivity rebuild produced no effective improvement; "
+ "local convergence detected"));
+ stopReason = PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ }
+
+ // 先确定当前最突出的可靠诊断误差,用其梯度回答“哪些参数最能改善
+ // 当前问题”;实际 LM 方向仍由完整残差梯度和 Jacobian 共同计算。
+ int dominantComponent = trustRegionDominantComponent(
+ current.breakdown, diagnosisThreshold);
+ const QVector* componentGradient = nullptr;
+ if(dominantComponent == TRUST_REGION_VERTICAL_COMPONENT) {
+ componentGradient = &verticalGradient;
+ } else if(dominantComponent == TRUST_REGION_HORIZONTAL_COMPONENT) {
+ componentGradient = &horizontalGradient;
+ } else if(dominantComponent == TRUST_REGION_SHAPE_COMPONENT) {
+ componentGradient = &shapeGradient;
+ }
+
+ // 主目标采用 0.5*||r||^2,其对参数的梯度为 J^T*r。这里不再叠加
+ // vertical/horizontal/shape,保证诊断分量不会改变真实接受目标。
+ QVector totalGradient(dimensions, 0.0);
+ for(int column = 0; column < dimensions; ++column) {
+ if(!jacobianColumnValid[column]) {
+ continue;
+ }
+ for(int row = 0; row < jacobian.size(); ++row) {
+ totalGradient[column] +=
+ jacobian[row][column] *
+ current.breakdown.residualVector[row];
+ }
+ }
+
+ // 每轮最多联合调整三个灵敏参数。按当前诊断梯度绝对值由大到小选取,
+ // 并剔除 Jacobian 响应过度共线的列,降低弱可辨识参数互相补偿的风险。
+ QVector selectedColumns;
+ QVector alreadyConsidered(dimensions, false);
+ for(int selection = 0; selection < qMin(3, dimensions); ++selection) {
+ int bestColumn = -1;
+ double bestScore = 0.0;
+ for(int column = 0; column < dimensions; ++column) {
+ if(alreadyConsidered[column] ||
+ !jacobianColumnValid[column]) {
+ continue;
+ }
+
+ double score = componentGradient
+ ? qAbs((*componentGradient)[column])
+ : qAbs(totalGradient[column]);
+ if(!isFiniteNumber(score) || score <= bestScore) {
+ continue;
+ }
+
+ bool excessivelyCorrelated = false;
+ for(int selectedIndex = 0;
+ selectedIndex < selectedColumns.size();
+ ++selectedIndex) {
+ if(trustRegionJacobianColumnCorrelation(
+ jacobian,
+ column,
+ selectedColumns[selectedIndex]) >
+ columnCorrelationLimit) {
+ excessivelyCorrelated = true;
+ break;
+ }
+ }
+ if(!excessivelyCorrelated) {
+ bestColumn = column;
+ bestScore = score;
+ }
+ }
+ if(bestColumn < 0 || bestScore <= 1.0e-12) {
+ break;
+ }
+ selectedColumns.append(bestColumn);
+ alreadyConsidered[bestColumn] = true;
+ }
+
+ // 诊断梯度接近零时,说明该分量在当前局部无法可靠选参,退回完整残差
+ // 梯度,但接受标准仍然只有真实 total,诊断值不会重复计入目标函数。
+ if(selectedColumns.isEmpty() && componentGradient) {
+ dominantComponent = TRUST_REGION_TOTAL_COMPONENT;
+ componentGradient = nullptr;
+ alreadyConsidered.fill(false, dimensions);
+ for(int selection = 0;
+ selection < qMin(3, dimensions);
+ ++selection) {
+ int bestColumn = -1;
+ double bestScore = 0.0;
+ for(int column = 0; column < dimensions; ++column) {
+ if(alreadyConsidered[column] ||
+ !jacobianColumnValid[column]) {
+ continue;
+ }
+ double score = qAbs(totalGradient[column]);
+ if(score <= bestScore) {
+ continue;
+ }
+ bool excessivelyCorrelated = false;
+ for(int selectedIndex = 0;
+ selectedIndex < selectedColumns.size();
+ ++selectedIndex) {
+ if(trustRegionJacobianColumnCorrelation(
+ jacobian,
+ column,
+ selectedColumns[selectedIndex]) >
+ columnCorrelationLimit) {
+ excessivelyCorrelated = true;
+ break;
+ }
+ }
+ if(!excessivelyCorrelated) {
+ bestColumn = column;
+ bestScore = score;
+ }
+ }
+ if(bestColumn < 0 || bestScore <= 1.0e-12) {
+ break;
+ }
+ selectedColumns.append(bestColumn);
+ alreadyConsidered[bestColumn] = true;
+ }
+ }
+
+ // 当前局部没有可用方向时先缩小半径并重建灵敏度;只有已经在最小
+ // 半径完整重建后仍无方向,才把它判定为局部最优。
+ if(selectedColumns.isEmpty()) {
+ if(trustRadius <= minimumTrustRadius * 1.01 &&
+ modelRebuiltAtMinimumRadius) {
+ stopReason = PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5);
+ damping = qMin(1.0e8, damping * 4.0);
+ rebuildRequested = true;
+ if(recordIneffectiveStep()) {
+ stopReason = PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ continue;
+ }
+
+ // 在选中参数子空间构造 LM 正规方程:
+ // (J^T*J + damping*diag(J^T*J))*step = -J^T*r。
+ // 对角缩放使不同参数列的灵敏度量级差异不会直接改变阻尼强弱。
+ const int selectedCount = selectedColumns.size();
+ QVector > normalMatrix(
+ selectedCount, QVector(selectedCount, 0.0));
+ QVector rightHandSide(selectedCount, 0.0);
+ for(int left = 0; left < selectedCount; ++left) {
+ int leftColumn = selectedColumns[left];
+ rightHandSide[left] = -totalGradient[leftColumn];
+ for(int right = 0; right < selectedCount; ++right) {
+ int rightColumn = selectedColumns[right];
+ for(int row = 0; row < jacobian.size(); ++row) {
+ normalMatrix[left][right] +=
+ jacobian[row][leftColumn] *
+ jacobian[row][rightColumn];
+ }
+ }
+ double diagonalScale = qMax(
+ 1.0e-10, normalMatrix[left][left]);
+ normalMatrix[left][left] += damping * diagonalScale;
+ }
+
+ QVector selectedStep;
+ bool solved = solveTrustRegionLinearSystem(
+ normalMatrix, rightHandSide, &selectedStep);
+ QVector coordinateStep(dimensions, 0.0);
+ if(solved) {
+ for(int i = 0; i < selectedCount; ++i) {
+ coordinateStep[selectedColumns[i]] = selectedStep[i];
+ }
+ }
+
+ double stepNorm = qSqrt(trustRegionSquaredNorm(coordinateStep));
+ if(!solved || !isFiniteNumber(stepNorm) ||
+ stepNorm < minimumCoordinateStep) {
+ // 正规方程退化时使用投影最速下降方向,仍只移动本轮已选择的参数。
+ coordinateStep.fill(0.0, dimensions);
+ double gradientNormSquared = 0.0;
+ for(int i = 0; i < selectedCount; ++i) {
+ int column = selectedColumns[i];
+ double stepDirection = -totalGradient[column];
+ if((current.coordinates[column] <= minimumCoordinateStep &&
+ stepDirection < 0.0) ||
+ (current.coordinates[column] >=
+ 1.0 - minimumCoordinateStep &&
+ stepDirection > 0.0)) {
+ stepDirection = 0.0;
+ }
+ coordinateStep[column] = stepDirection;
+ gradientNormSquared += stepDirection * stepDirection;
+ }
+ double gradientNorm = qSqrt(gradientNormSquared);
+ if(gradientNorm > minimumCoordinateStep) {
+ double scale = trustRadius / gradientNorm;
+ for(int i = 0; i < selectedCount; ++i) {
+ int column = selectedColumns[i];
+ coordinateStep[column] *= scale;
+ }
+ }
+ stepNorm = qSqrt(trustRegionSquaredNorm(coordinateStep));
+ }
+
+ // LM 解只给出局部模型建议方向;若超出当前信赖半径,保持方向不变并
+ // 等比例截短,避免一次试算离开 Jacobian 有效的局部区域。
+ if(stepNorm > trustRadius && stepNorm > 0.0) {
+ double scale = trustRadius / stepNorm;
+ for(int i = 0; i < coordinateStep.size(); ++i) {
+ coordinateStep[i] *= scale;
+ }
+ }
+
+ // 将 LM 步长投影到用户给定的参数范围,实际用于预测下降的也是投影后步长。
+ QVector candidateCoordinates = current.coordinates;
+ for(int i = 0; i < dimensions; ++i) {
+ candidateCoordinates[i] = qBound(
+ 0.0,
+ current.coordinates[i] + coordinateStep[i],
+ 1.0);
+ coordinateStep[i] = candidateCoordinates[i] -
+ current.coordinates[i];
+ }
+ stepNorm = qSqrt(trustRegionSquaredNorm(coordinateStep));
+
+ // 用线性模型 r_new ~= r_current + J*step 预测残差,再用平方能量
+ // 的下降量与真实候选下降量比较,作为调整阻尼和半径的依据。
+ QVector predictedResidual =
+ current.breakdown.residualVector;
+ for(int row = 0; row < jacobian.size(); ++row) {
+ for(int column = 0; column < dimensions; ++column) {
+ predictedResidual[row] +=
+ jacobian[row][column] * coordinateStep[column];
+ }
+ }
+ double predictedReduction = 0.5 *
+ (trustRegionSquaredNorm(current.breakdown.residualVector) -
+ trustRegionSquaredNorm(predictedResidual));
+
+ // 无实际移动或模型预测不下降时没有必要调用昂贵求解器。将它按一次
+ // 拒绝处理,并在连续发生后重建灵敏度,防止继续沿失效模型试算。
+ if(stepNorm < minimumCoordinateStep ||
+ !isFiniteNumber(predictedReduction) ||
+ predictedReduction <= 1.0e-14) {
+ trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5);
+ damping = qMin(1.0e8, damping * 4.0);
+ ++consecutiveRejectedSteps;
+ if(consecutiveRejectedSteps >= 2) {
+ if(trustRadius <= minimumTrustRadius * 1.01 &&
+ modelRebuiltAtMinimumRadius) {
+ stopReason = PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ rebuildRequested = true;
+ }
+ if(recordIneffectiveStep()) {
+ stopReason = PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ continue;
+ }
+
+ TrustRegionEvaluation candidate;
+ candidate.coordinates = candidateCoordinates;
+ candidate.parameters = parametersFromCoordinates(candidate.coordinates);
+ candidate.valid = evaluateTrustRegionPoint(
+ candidate.parameters,
+ &candidate.fitness,
+ &candidate.breakdown,
+ &candidate.curve,
+ &candidate.elapsedMs);
+
+ if(!candidate.valid) {
+ // 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和
+ // 对应误差快照,再缩小信赖域;连续失败达到上限才终止整个拟合。
+ ++consecutiveSolverFailures;
+ ++consecutiveRejectedSteps;
+ trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5);
+ damping = qMin(1.0e8, damping * 4.0);
+ writeTraceRow(m_currentIteration,
+ -1,
+ "trust_region_candidate",
+ candidate.parameters,
+ candidate.fitness,
+ false,
+ candidate.elapsedMs,
+ std::numeric_limits::quiet_NaN(),
+ "solver_invalid",
+ current.parameters,
+ current.fitness,
+ nullptr);
+ restoreEvaluationState(current);
+ if(consecutiveRejectedSteps >= 2) {
+ rebuildRequested = true;
+ }
+ if(consecutiveSolverFailures >= m_maxConsecutiveFailures) {
+ stopReason = PSO_CONSECUTIVE_FAILURES;
+ break;
+ }
+ if(recordIneffectiveStep()) {
+ stopReason = PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ continue;
+ }
+
+ consecutiveSolverFailures = 0;
+ // 有效候选即使最终被拒绝,也提供了一条真实割线,可用于修正下一轮
+ // 局部模型;是否成为新工作点仍只由下面的 total 严格比较决定。
+ const AutoFitObjectiveBreakdown oldBreakdown = current.breakdown;
+ updateTrustRegionJacobian(
+ &jacobian,
+ oldBreakdown.residualVector,
+ candidate.breakdown.residualVector,
+ coordinateStep);
+ if(oldBreakdown.verticalReliable &&
+ candidate.breakdown.verticalReliable &&
+ !oldBreakdown.registrationAmbiguous &&
+ !candidate.breakdown.registrationAmbiguous) {
+ updateTrustRegionScalarGradient(
+ &verticalGradient,
+ oldBreakdown.verticalCommonBias,
+ candidate.breakdown.verticalCommonBias,
+ coordinateStep);
+ }
+ if(oldBreakdown.horizontalReliable &&
+ candidate.breakdown.horizontalReliable &&
+ !oldBreakdown.registrationAmbiguous &&
+ !candidate.breakdown.registrationAmbiguous) {
+ updateTrustRegionScalarGradient(
+ &horizontalGradient,
+ oldBreakdown.horizontalPhysicalShift,
+ candidate.breakdown.horizontalPhysicalShift,
+ coordinateStep);
+ }
+ updateTrustRegionScalarGradient(
+ &shapeGradient,
+ oldBreakdown.shapeLoss,
+ candidate.breakdown.shapeLoss,
+ coordinateStep);
+
+ // reductionRatio 衡量局部线性模型的可信度:接近 1 表示预测准确;
+ // 值较小表示虽然可能下降,但模型低估了非线性,需要收紧下一步。
+ double actualReduction = 0.5 *
+ (current.fitness * current.fitness -
+ candidate.fitness * candidate.fitness);
+ double reductionRatio = actualReduction / predictedReduction;
+ bool accepted = candidate.fitness < current.fitness;
+ QString componentName = trustRegionComponentName(dominantComponent);
+
+ if(accepted) {
+ // 真实总误差下降后才正式替换 current,并同步发布参数、曲线和诊断。
+ // 模型预测可靠时减小阻尼并可扩大半径,预测较差时保守收缩。
+ current = candidate;
+ publishAcceptedPoint(current);
+ restoreEvaluationState(current);
+ ++acceptedSinceRebuild;
+ movementSinceRebuild += stepNorm;
+ consecutiveRejectedSteps = 0;
+ m_convergenceHistory.append(current.fitness);
+
+ if(reductionRatio > 0.75) {
+ damping = qMax(1.0e-8, damping * 0.5);
+ if(stepNorm >= trustRadius * 0.8) {
+ trustRadius = qMin(
+ maximumTrustRadius, trustRadius * 1.6);
+ }
+ } else if(reductionRatio > 0.25) {
+ damping = qMax(1.0e-8, damping * 0.8);
+ } else {
+ damping = qMin(1.0e8, damping * 2.0);
+ trustRadius = qMax(
+ minimumTrustRadius, trustRadius * 0.75);
+ }
+
+ if(acceptedSinceRebuild >= 6 ||
+ movementSinceRebuild >= 0.30) {
+ rebuildRequested = true;
+ }
+ modelRebuiltAtMinimumRadius = false;
+ } else {
+ // 拒绝时 candidate 只保留在 trace 中,DataManager 和内存状态都恢复
+ // 到 current。连续拒绝说明割线模型可能失真,因此请求重新试算灵敏度。
+ ++consecutiveRejectedSteps;
+ damping = qMin(1.0e8, damping * 4.0);
+ trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5);
+ restoreEvaluationState(current);
+ if(consecutiveRejectedSteps >= 2) {
+ rebuildRequested = true;
+ }
+ }
+
+ // 候选只要更优就继续作为 current 保存;是否足以解除停滞,则统一
+ // 相对上一次有效改善基准判断。拒绝和微小改善都会累计无效次数。
+ const bool effectiveImprovement =
+ registerEffectiveImprovement(current.fitness);
+ if(!effectiveImprovement && recordIneffectiveStep()) {
+ stopReason = PSO_LOCAL_OPTIMUM;
+ }
+
+ writeTraceRow(m_currentIteration,
+ -1,
+ "trust_region_candidate",
+ candidate.parameters,
+ candidate.fitness,
+ true,
+ candidate.elapsedMs,
+ std::numeric_limits::quiet_NaN(),
+ accepted
+ ? QString("accepted_%1").arg(componentName)
+ : QString("rejected_%1").arg(componentName),
+ current.parameters,
+ current.fitness,
+ &candidate.breakdown);
+
+ emit logMessageGenerated(
+ tr("Iteration %1: focus=%2, parameters=%3, error=%4, result=%5")
+ .arg(iteration + 1)
+ .arg(componentName)
+ .arg(selectedColumns.size())
+ .arg(candidate.fitness, 0, 'e', 4)
+ .arg(accepted ? tr("accepted") : tr("rejected")));
+ emit progressUpdated(iteration + 1, m_globalBestFitness);
+
+ if(stopReason == PSO_LOCAL_OPTIMUM) {
+ break;
+ }
+
+ if(current.fitness < m_targetError) {
+ stopReason = PSO_TARGET_ACHIEVED;
+ break;
+ }
+ if(trustRadius <= minimumTrustRadius * 1.01 &&
+ consecutiveRejectedSteps >= 2) {
+ if(modelRebuiltAtMinimumRadius) {
+ stopReason = PSO_LOCAL_OPTIMUM;
+ break;
+ }
+ rebuildRequested = true;
+ }
+ }
+
+ if(completedIterations > 0) {
+ m_currentIteration = completedIterations - 1;
+ }
+ restoreEvaluationState(current);
+
+ if(m_shouldStop) {
+ return PSO_USER_STOPPED;
+ }
+ if(current.fitness < m_targetError) {
+ return PSO_TARGET_ACHIEVED;
+ }
+ if(stopReason == PSO_CONSECUTIVE_FAILURES ||
+ stopReason == PSO_LOCAL_OPTIMUM ||
+ stopReason == PSO_OPTIMIZATION_FAILED) {
+ return stopReason;
+ }
+ return PSO_MAX_ITERATIONS;
+}
+
+void nmCalculationAutoFitPSO::updateParticle(int particleIndex)
+{
+ if(particleIndex < 0 || particleIndex >= m_swarm.size()) return;
+
+ AutoFitParticle& particle = m_swarm[particleIndex];
+
+ // 单粒子真实评价入口。
+ // 这里调用 evaluateFitness(),因此会真实写 DataManager、调用求解器、计算误差。
+ // 被代理模型筛掉的粒子不会进入这个函数。
+ particle.evaluatedThisIteration = false;
+ particle.lastEvaluationSuccess = false;
+ particle.lastEvaluationElapsedMs = -1;
+ particle.pbestRelativeImprovementThisIteration = 0.0;
+ particle.selectedForSolver = true;
+
+ if(particle.screeningDecision.isEmpty()) {
+ particle.screeningDecision = "full_solver";
+ }
+
+ bool reuseInitialSolution = m_currentIteration == 0 &&
+ particleIndex == 0 &&
+ m_hasValidUserSolution &&
+ m_userInitialFitness < 1e9 &&
+ particle.position.size() == m_userInitialSolution.size();
+
+ for(int i = 0; reuseInitialSolution && i < particle.position.size(); ++i) {
+ double tolerance = qMax(1.0e-12, qAbs(m_userInitialSolution[i]) * 1.0e-12);
+ reuseInitialSolution = qAbs(particle.position[i] - m_userInitialSolution[i]) <= tolerance;
+ }
+
+ if(reuseInitialSolution) {
+ // 初始解在进入粒子群前已经真实求解过。第一代第0号粒子位置完全相同,
+ // 直接复用真实误差和曲线,避免一次重复 DLL 调用且不改变 PSO 数学状态。
+ particle.fitness = m_userInitialFitness;
+ particle.currentLogLogData = m_userInitialLogLogData;
+ particle.currentObjectiveBreakdown = m_userInitialObjectiveBreakdown;
+ particle.lastEvaluationElapsedMs = 0;
+ particle.evaluatedThisIteration = true;
+ particle.lastEvaluationSuccess = true;
+ particle.screeningDecision = "initial_solution_cache";
+ emit logMessageGenerated(tr("Particle 1 reused the verified initial solution"));
+ } else {
+ QTime evalTimer;
+ evalTimer.start();
+ particle.fitness = evaluateFitness(particle.position);
+ particle.currentLogLogData = m_lastEvaluatedLogLogData;
+ particle.currentObjectiveBreakdown = m_lastObjectiveBreakdown;
+ particle.lastEvaluationElapsedMs = evalTimer.elapsed();
+ particle.evaluatedThisIteration = true;
+ particle.lastEvaluationSuccess = (particle.fitness < 1e9);
+ m_totalEvaluations++;
+
+ if(particle.fitness < 1e9) {
+ m_successfulEvaluations++;
+ }
+ }
+
+ // 更新个体最优 pbest。这里使用的是真实求解器误差 particle.fitness,
+ // 不是代理模型给出的 surrogateObjective。
+ double previousBestFitness = particle.bestFitness;
+
+ if(particle.fitness < previousBestFitness) {
+ particle.pbestRelativeImprovementThisIteration = previousBestFitness >= 1e9
+ ? 1.0
+ : (previousBestFitness - particle.fitness) /
+ qMax(1e-10, qAbs(previousBestFitness));
+
+ // 如果当前位置尚未成为新的真实全局最优,而上一代存在尚未真实验证、且代理
+ // 仍判断更优的 guide,就保留它继续引导速度;真实 pbest 仍照常更新。
+ bool preserveSurrogateGuide = false;
+
+ bool currentBeatsGlobalBest = particle.fitness < m_globalBestFitness;
+
+ if(!currentBeatsGlobalBest &&
+ isSurrogateScreeningEnabled() &&
+ particle.guideBestFromSurrogate &&
+ isFiniteNumber(particle.guideBestObjective) &&
+ isFiniteNumber(particle.surrogateObjective)) {
+ double requiredImprovement = qMax(1.0e-10,
+ qAbs(particle.guideBestObjective) *
+ kSurrogateGuidePbestMinRelativeImprovement);
+ preserveSurrogateGuide = particle.surrogateObjective -
+ particle.guideBestObjective > requiredImprovement;
+ }
+
+ particle.bestFitness = particle.fitness;
+ particle.bestPosition = particle.position;
+ particle.bestLogLogData = particle.currentLogLogData;
+ particle.bestObjectiveBreakdown = particle.currentObjectiveBreakdown;
+
+ if(!preserveSurrogateGuide) {
+ particle.guideBestPosition = particle.position;
+ particle.guideBestObjective = isFiniteNumber(particle.surrogateObjective)
+ ? particle.surrogateObjective
+ : particle.fitness;
+ particle.guideBestFromSurrogate = false;
+ }
+
+ DEBUG_OUT(QString("Particle %1 improved: error = %2")
+ .arg(particleIndex).arg(particle.fitness, 0, 'e', 4));
+ }
+}
+
+void nmCalculationAutoFitPSO::updateGlobalBest()
+{
+ // 保存上一轮的全局最优,用于后续自适应参数调整等
+ m_previousBestFitness = m_globalBestFitness;
+ bool globalBestUpdated = false;
+
+ // 遍历所有粒子,寻找比当前 global best 更好的个体最优。
+ // 注意:particle.bestFitness 只有在真实求解器评价成功后才会更新。
+ for(int i = 0; i < m_swarm.size(); ++i) {
+ const AutoFitParticle& particle = m_swarm[i];
+
+ // 只要个体最优比当前全局最优小,就认为是更好的解
+ if(particle.bestFitness < m_globalBestFitness) {
+
+ double improvement = m_globalBestFitness - particle.bestFitness;
+ double relativeImprovement =
+ improvement / qMax(1e-10, qAbs(m_globalBestFitness));
+
+ // 区分显著改进和微小改进,但无论如何都会更新全局最优
+ if(relativeImprovement > m_improvementThreshold) {
+ DEBUG_OUT(QString(tr("Global best updated with %1% improvement: %2"))
+ .arg(relativeImprovement * 100.0, 0, 'f', 3)
+ .arg(particle.bestFitness, 0, 'e', 4));
+ } else {
+ DEBUG_OUT(QString(tr("Global best updated (minor improvement %1% < %2%) to %3"))
+ .arg(relativeImprovement * 100.0, 0, 'f', 3)
+ .arg(m_improvementThreshold * 100.0, 0, 'f', 2)
+ .arg(particle.bestFitness, 0, 'e', 4));
+ }
+
+ // 无论相对改进是否超过阈值,都要更新全局最优
+ m_globalBestFitness = particle.bestFitness;
+ m_globalBestPosition = particle.bestPosition;
+ m_globalBestLogLogData = particle.bestLogLogData;
+ m_globalBestObjectiveBreakdown = particle.bestObjectiveBreakdown;
+ globalBestUpdated = true;
+ emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
+ }
+ }
+
+ // 只有在本轮没有找到任何更好的解时才考虑恢复用户初始解
+ if(!globalBestUpdated && m_hasValidUserSolution && m_userInitialFitness < m_globalBestFitness) {
+
+ double improvement = m_globalBestFitness - m_userInitialFitness;
+ double relativeImprovement =
+ improvement / qMax(1e-10, qAbs(m_globalBestFitness));
+
+ DEBUG_OUT("Elite protection: Restoring user initial solution as global best");
+ DEBUG_OUT(QString("Elite solution is better than current best by %1%")
+ .arg(relativeImprovement * 100.0, 0, 'f', 3));
+
+ m_globalBestFitness = m_userInitialFitness;
m_globalBestPosition = m_userInitialSolution;
m_globalBestLogLogData = m_userInitialLogLogData;
+ m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
}
}
@@ -3787,6 +5375,7 @@ void nmCalculationAutoFitPSO::updateVelocityAndPosition()
int paramIndex = m_enabledParamIndices[j];
double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
double maxVel = range * VELOCITY_LIMIT_FACTOR;
+
particle.velocity[j] = qMax(-maxVel, qMin(maxVel, particle.velocity[j]));
// 更新位置
@@ -3961,7 +5550,7 @@ double nmCalculationAutoFitPSO::evaluateFitness(const QVector& parameter
// 1. 校验粒子参数是否在用户设置的上下界和基本物理范围内;
// 2. 将参数写入 DataManager 的储层/目标井对象;
// 3. 调用真实数值求解器,生成模拟结果;
- // 4. 从目标井读取模拟后的 result log-log 曲线;
+ // 4. 从本次求解任务读取目标井 result log-log 曲线;
// 5. 与目标 history log-log 曲线计算误差,误差越小代表拟合越好。
//
// 返回 1e10 表示该粒子评价失败或结果不可用。PSO 会把它当成很差的解。
@@ -3969,6 +5558,7 @@ double nmCalculationAutoFitPSO::evaluateFitness(const QVector& parameter
static int callCount = 0;
callCount++;
m_lastEvaluatedLogLogData.clear();
+ m_lastObjectiveBreakdown = AutoFitObjectiveBreakdown();
try {
DEBUG_OUT(QString("%1: Call #%2 - Starting evaluation with %3 parameters")
@@ -4066,6 +5656,20 @@ double nmCalculationAutoFitPSO::evaluateFitness(const QVector& parameter
return 1e10;
}
+ // Dfc 和裂缝半长都通过 PEBI 裂缝数组传入,不属于每次求解都会重新组装的 Base/CS 参数。
+ // 勾选任一裂缝参数时刷新网格输出,保证本次真实试算使用新的导流能力和端点坐标。
+ const bool fractureGridParameterSelected =
+ (m_parameterSelected.size() > 8 && m_parameterSelected[8]) ||
+ (m_parameterSelected.size() > 9 && m_parameterSelected[9]);
+ if(fractureGridParameterSelected) {
+ nmCalculationPebiGrid* pebiGrid = nmCalculationPebiGrid::getInstance();
+ if(!pebiGrid || !pebiGrid->generateOutputPara()) {
+ DEBUG_OUT(QString("%1: Call #%2 - Failed to refresh PEBI fracture parameters")
+ .arg(funcName).arg(callCount));
+ return 1e10;
+ }
+ }
+
// 4. 运行求解器。真实求解器偶发失败时允许重试,避免一次 DLL 调用异常
// 直接让整个粒子评价失败。
QVector> solverResult;
@@ -4122,24 +5726,11 @@ double nmCalculationAutoFitPSO::evaluateFitness(const QVector& parameter
return 1e10;
}
- // 5. 获取 LogLog 数据。runSolver() 会更新 DataManager 中目标井的计算结果,
- // 这里再从目标井读取 resultLogLogData 作为模拟曲线。
- QVector> resultLogLogData;
+ // 5. 获取 LogLog 数据。runSolverDll() 直接从求解任务复制目标井曲线,
+ // 不再依赖 DataManager 中可能被其它井或上一粒子改写的共享结果。
+ QVector> resultLogLogData = m_lastEvaluatedLogLogData;
try {
- nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
-
- if(!pTargetWell) {
- DEBUG_OUT(QString("%1: Call #%2 - Target well '%3' NOT FOUND")
- .arg(funcName).arg(callCount).arg(m_targetWellName));
- return 1e10;
- }
-
- DEBUG_OUT(QString("%1: Call #%2 - Target well found: %3")
- .arg(funcName).arg(callCount).arg(m_targetWellName));
-
- resultLogLogData = pTargetWell->getResultLogLog();
-
if(!validateLogLogData(resultLogLogData)) {
DEBUG_OUT(QString("%1: Call #%2 - LogLog data VALIDATION FAILED")
.arg(funcName).arg(callCount));
@@ -4241,7 +5832,7 @@ void nmCalculationAutoFitPSO::updateReservoirParameters(const QVector