diff --git a/README.md b/README.md index 4aa5bf9cf..3e410e0cc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ FLORIS is a controls-focused wind farm simulation software incorporating steady-state engineering wake models into a performance-focused Python framework. It has been in active development at NREL since 2013 and the latest -release is [FLORIS v3.4.1](https://github.com/NREL/floris/releases/latest). +release is [FLORIS v3.5](https://github.com/NREL/floris/releases/latest). Online documentation is available at https://nrel.github.io/floris. The software is in active development and engagement with the development team @@ -71,7 +71,7 @@ and importing FLORIS: version VERSION - 3.4 + 3.5 FILE ~/floris/floris/__init__.py diff --git a/docs/architecture.md b/docs/architecture.md index 88da05b0e..682aa5c8b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,17 +1,29 @@ # Architecture and Design -Two fundamental ideas define the design of the FLORIS software: +At the outset of the design of the FLORIS software, a few fundamental ideas were identified +that should continue to guide future design decisions. These characteristics should never +be violated, and ongoing work should strive to meet these ideas and expand on them as much +as possible. -- Modularity in wake model formulation - - Mathematical formulation should be straightforward to include +- Modularity in wake model formulation: + - New mathematical formulation should be straightforward to incorporate. - Requisite solver and grid data structures should not conflict with other existing - wake models -- Management of abstraction + wake models. +- Any new feature or work should not affect an existing feature: + - Low level code should be reused as much as possible, but high level code should rarely + be repurposed. + - It is expected that a new feature will include a new user entry point + at the highest level. + - Avoid flags and if-statements that allow using one high-level routine for multiple unrelated + tasks. + - When in doubt, create a new pipeline from the user-level API to the low level implementation + and refactor to consolidate, if necessary, afterwards. +- Management of abstraction: - Low level code is opaque but well tested and exercised; it should be very computationally - efficient with low algorithmic complexity + efficient with low algorithmic complexity. - High level code should be expressive and clear even if it results in verbose or less - efficient code + efficient code. The FLORIS software consists of two primary high-level packages and a few other low level packages. The internal structure and hierarchy is described below. diff --git a/docs/code_quality.ipynb b/docs/code_quality.ipynb index 24c5353a1..d6a4322ee 100644 --- a/docs/code_quality.ipynb +++ b/docs/code_quality.ipynb @@ -1,6 +1,7 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -62,47 +63,66 @@ "data = [\n", " (\"df25a9cfacd3d652361d2bd37f568af00acb2631\", \"df25a9cf\", datetime(2021, 12, 29), 1.2691, 1.2584, 1.6432, None, 0.4344, \"df25a9cf\"),\n", " (\"b797390a43298a815f3ff57955cfdc71ecf3e866\", \"b797390a\", datetime(2022, 1, 3), 0.6867, 1.2354, 1.8026, None, 0.2993, \"b797390a\"),\n", - " (\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\", \"01a02d5f\", datetime(2022, 1, 4), 0.4048, 0.8909, 1.4921, None, 0.3022, \"01a02d5f\"),\n", - " (\"dd847210082035d43b0273ae63a76a53cb8d2e12\", \"dd847210\", datetime(2022, 1, 6), 0.4004, 0.8622, 1.4506, None, 0.3627, \"dd847210\"),\n", - " (\"33779269e98cc882a5f066c462d8ec1eadf37a1a\", \"33779269\", datetime(2022, 1, 10), 0.4025, 0.8954, 1.5164, None, 0.3690, \"33779269\"),\n", - " (\"12890e029a7155b074b9b325d320d1798338e287\", \"12890e02\", datetime(2022, 1, 11), 0.3979, 0.9134, 1.5469, None, 0.3682, \"12890e02\"),\n", - " (\"66dafc08bd620d96deda7d526b0e4bfc3b086650\", \"66dafc08\", datetime(2022, 1, 12), 0.4175, 0.8834, 1.5187, None, 0.3709, \"66dafc08\"),\n", - " (\"a325819b3b03b84bd76ad455e3f9b4600744ba14\", \"a325819b\", datetime(2022, 1, 13), 0.4207, 0.8781, 1.5001, None, 0.3709, \"a325819b\"),\n", - " (\"8a2c1a610295c007f0222ce737723c341189811d\", \"8a2c1a61\", datetime(2022, 1, 14), 0.4108, 0.8914, 1.5599, None, 0.3708, \"8a2c1a61\"),\n", - " (\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\", \"c6bc79b0\", datetime(2022, 1, 14), 0.4172, 0.8813, 1.4888, None, 0.3701, \"c6bc79b0\"),\n", - " (\"03e1f461c152e4f221fe92c834f2787680cf5772\", \"03e1f461\", datetime(2022, 1, 18), 0.4294, 0.8760, 1.5124, 1.8728, 0.3673, \"PR #56\"),\n", - " (\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\", \"9e96d6c4\", datetime(2022, 1, 19), 0.4389, 0.8505, 1.4700, 1.8529, 0.3825, \"v3.0rc1\"),\n", - " (\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\", \"2a98428f\", datetime(2022, 2, 15), 0.2548, 0.8753, 1.5254, 1.8375, 0.3824, \"PR #317\"),\n", - " (\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\", \"9b4e85cf\", datetime(2022, 3, 1), 0.2687, 0.9676, 1.5895, 1.8790, 0.1572, \"v3.0\"),\n", - " (\"d18f4d263ecabf502242592f9d60815a07c7b89c\", \"d18f4d26\", datetime(2022, 3, 4), 0.2742, 0.9402, 1.5835, 1.8870, 0.1572, \"v3.0.1\"),\n", - " (\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\", \"a23241bb\", datetime(2022, 4, 6), 0.2609, 0.9793, 1.6281, 1.8673, 0.1682, \"v3.1\"),\n", - " (\"c2006b0011a5df036c306c15e75763ec492dafda\", \"c2006b00\", datetime(2022, 6, 22), 0.2733, 0.9668, 1.6002, 1.8838, 0.1681, \"v3.1.1\"),\n", - " (\"0c2adf3e702b6427da946a6ba9dbedbea22738be\", \"0c2adf3e\", datetime(2022, 9, 16), 0.2727, 0.9613, 1.5977, 1.8369, 0.1502, \"v3.2\"),\n", - " (\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\", \"39c46600\", datetime(2022, 11, 20), 0.2729, 0.9561, 1.5817, 1.8541, 0.1899, \"v3.2.1\"),\n", - " (\"8436fd78b002e5792f5d0dd1409332d171036d49\", \"8436fd78\", datetime(2023, 2, 8), 0.2753, 0.9718, 1.5985, 1.8721, 0.1905, \"v3.2.2\"),\n", - " (\"07a45b66c5facfea06c40bd82e34040c97560640\", \"07a45b66\", datetime(2023, 2, 8), 0.2763, 0.9837, 1.5750, 1.8805, 0.1972, \"07a45b66\"),\n", - " (\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\", \"1d84538c\", datetime(2023, 2, 22), 0.2747, 0.9457, 1.5743, 1.8628, 0.0000, \"1d84538c\"),\n", - " (\"4d528a3d6456621a382d409b5145a877b5414b88\", \"4d528a3d\", datetime(2023, 2, 23), 0.2669, 0.9502, 1.5503, 1.8683, 0.0000, \"4d528a3d\"),\n", - " (\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\", \"8c637b36\", datetime(2023, 2, 27), 0.2918, 0.9974, 1.5609, 1.8825, 0.0000, \"8c637b36\"),\n", - " (\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\", \"4d23fa6d\", datetime(2023, 2, 27), 0.2962, 0.9924, 1.5983, 1.8535, 0.0000, \"4d23fa6d\"),\n", - " (\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\", \"015f6874\", datetime(2023, 3, 1), 0.2990, 1.0068, 1.5856, 1.8722, 0.0000, \"015f6874\"),\n", - " (\"26f06d449da208ce64724b1463b07ad20746cbdc\", \"26f06d44\", datetime(2023, 3, 6), 0.2701, 0.9652, 1.5992, 1.8506, 0.0000, \"26f06d44\"),\n", - " (\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\", \"6b9d6bb8\", datetime(2023, 3, 6), 0.2964, 0.9775, 1.6261, 1.8816, 0.0000, \"6b9d6bb8\"),\n", - " (\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\", \"b796bd0f\", datetime(2023, 3, 6), 0.2692, 0.9455, 1.5827, 1.8598, 0.0000, \"b796bd0f\"),\n", - " (\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\", \"780aef7c\", datetime(2023, 3, 7), 0.2980, 0.9909, 1.5796, 1.8696, 0.0000, \"780aef7c\"),\n", - " (\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\", \"9f93ad9b\", datetime(2023, 3, 7), 0.2985, 0.9925, 1.5896, 1.8813, 0.0000, \"9f93ad9b\"),\n", - " (\"16628a0ba45a675df762245694e0a7666a3478f8\", \"16628a0b\", datetime(2023, 3, 7), 0.3013, 0.9700, 1.5791, 1.8950, 0.1972, \"v3.3\"),\n", - " (\"01684c8559604344bd09791268131819a09770a8\", \"01684c85\", datetime(2023, 3, 17), 0.3016, 0.9931, 1.5986, 1.8960, 0.0000, \"01684c85\"),\n", - " (\"e9231fb893c765b723fa4c1e087a58761b6aa471\", \"e9231fb8\", datetime(2023, 3, 20), 0.2974, 0.9963, 1.5817, 1.8798, 0.0000, \"e9231fb8\"),\n", - " (\"219889e243ffc69c71b6f7747f5af751d5694de1\", \"219889e2\", datetime(2023, 3, 23), 0.2897, 1.0008, 1.5651, 1.8983, 0.0000, \"219889e2\"),\n", - " (\"6124d2a82a7a823722210bc2e8516d355ba19eb3\", \"6124d2a8\", datetime(2023, 4, 5), 0.2971, 0.9918, 1.5904, 1.9332, 0.0000, \"6124d2a8\"),\n", - " (\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\", \"f6e4287f\", datetime(2023, 4, 25), 0.3045, 0.9905, 1.6114, 1.8999, 0.0000, \"f6e4287f\"),\n", - " (\"f2797fef396f2f19b02abb1f9555b678dac614f1\", \"f2797fef\", datetime(2023, 4, 25), 0.3071, 1.0112, 1.5760, 1.8921, 0.0000, \"f2797fef\"),\n", - " (\"b4e538f530048fec58eaca5170be82c67dbdcceb\", \"b4e538f5\", datetime(2023, 4, 25), 0.2924, 0.9751, 1.6105, 1.9043, 0.0000, \"b4e538f5\"),\n", - " (\"68820b715ed6b2c981aa11d29c0102e879280d79\", \"68820b71\", datetime(2023, 4, 25), 0.3013, 0.9936, 1.6038, 1.9069, 0.0000, \"68820b71\"),\n", - " (\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\", \"03deffed\", datetime(2023, 4, 25), 0.2930, 0.9882, 1.6013, 1.9015, 0.0000, \"03deffed\"),\n", - " (\"0d2bfecc271d561f67050659684b4797af8ee740\", \"0d2bfecc\", datetime(2023, 4, 25), 0.3041, 1.0009, 1.5853, 1.8890, 0.0000, \"0d2bfecc\"),\n", - " (\"1d03a465593f56c99a64a576d185d4ed17b659f2\", \"1d03a465\", datetime(2023, 4, 25), 0.3058, 0.9970, 1.5849, 1.8224, 0.0000, \"1d03a465\"),\n", + " (\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\", \"01a02d5f\", datetime(2022, 1, 4), 0.3697, 0.8080, 1.3633, None, 0.3022, \"01a02d5f\"),\n", + " (\"dd847210082035d43b0273ae63a76a53cb8d2e12\", \"dd847210\", datetime(2022, 1, 6), 0.3551, 0.8127, 1.3390, None, 0.3627, \"dd847210\"),\n", + " (\"33779269e98cc882a5f066c462d8ec1eadf37a1a\", \"33779269\", datetime(2022, 1, 10), 0.3544, 0.8298, 1.4116, None, 0.3690, \"33779269\"),\n", + " (\"12890e029a7155b074b9b325d320d1798338e287\", \"12890e02\", datetime(2022, 1, 11), 0.3511, 0.8392, 1.3829, None, 0.3682, \"12890e02\"),\n", + " (\"66dafc08bd620d96deda7d526b0e4bfc3b086650\", \"66dafc08\", datetime(2022, 1, 12), 0.3807, 0.8212, 1.3877, None, 0.3709, \"66dafc08\"),\n", + " (\"a325819b3b03b84bd76ad455e3f9b4600744ba14\", \"a325819b\", datetime(2022, 1, 13), 0.3741, 0.8185, 1.3809, None, 0.3709, \"a325819b\"),\n", + " (\"8a2c1a610295c007f0222ce737723c341189811d\", \"8a2c1a61\", datetime(2022, 1, 14), 0.3689, 0.8106, 1.4001, None, 0.3708, \"8a2c1a61\"),\n", + " (\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\", \"c6bc79b0\", datetime(2022, 1, 14), 0.3652, 0.8013, 1.4334, None, 0.3701, \"c6bc79b0\"),\n", + " (\"03e1f461c152e4f221fe92c834f2787680cf5772\", \"03e1f461\", datetime(2022, 1, 18), 0.3688, 0.7887, 1.4096, 1.5801, 0.3673, \"PR #56\"),\n", + " (\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\", \"9e96d6c4\", datetime(2022, 1, 19), 0.3807, 0.7835, 1.3760, 1.5815, 0.3825, \"v3.0rc1\"),\n", + " (\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\", \"2a98428f\", datetime(2022, 2, 15), 0.2362, 0.7966, 1.3459, 1.5618, 0.3824, \"PR #317\"),\n", + " (\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\", \"9b4e85cf\", datetime(2022, 3, 1), 0.2479, 0.8836, 1.4280, 1.6094, 0.1572, \"v3.0\"),\n", + " (\"d18f4d263ecabf502242592f9d60815a07c7b89c\", \"d18f4d26\", datetime(2022, 3, 4), 0.2471, 0.8861, 1.4601, 1.5769, 0.1572, \"v3.0.1\"),\n", + " (\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\", \"a23241bb\", datetime(2022, 4, 6), 0.2450, 0.8902, 1.4467, 1.5751, 0.1682, \"v3.1\"),\n", + " (\"c2006b0011a5df036c306c15e75763ec492dafda\", \"c2006b00\", datetime(2022, 6, 22), 0.2507, 0.8754, 1.4494, 1.6140, 0.1681, \"v3.1.1\"),\n", + " (\"0c2adf3e702b6427da946a6ba9dbedbea22738be\", \"0c2adf3e\", datetime(2022, 9, 16), 0.2524, 0.8878, 1.4293, 1.5771, 0.1502, \"v3.2\"),\n", + " (\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\", \"39c46600\", datetime(2022, 11, 20), 0.2521, 0.9094, 1.4539, 1.5681, 0.1899, \"v3.2.1\"),\n", + " (\"8436fd78b002e5792f5d0dd1409332d171036d49\", \"8436fd78\", datetime(2023, 2, 8), 0.2583, 0.8718, 1.4540, 1.5907, 0.1905, \"v3.2.2\"),\n", + " (\"07a45b66c5facfea06c40bd82e34040c97560640\", \"07a45b66\", datetime(2023, 2, 8), 0.2486, 0.8641, 1.4610, 1.6280, 0.1972, \"07a45b66\"),\n", + " (\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\", \"1d84538c\", datetime(2023, 2, 22), 0.2515, 0.8864, 1.4248, 1.5819, 0.0000, \"1d84538c\"),\n", + " (\"4d528a3d6456621a382d409b5145a877b5414b88\", \"4d528a3d\", datetime(2023, 2, 23), 0.2557, 0.8969, 1.4104, 1.5799, 0.0000, \"4d528a3d\"),\n", + " (\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\", \"8c637b36\", datetime(2023, 2, 27), 0.2773, 0.9240, 1.4645, 1.5997, 0.0000, \"8c637b36\"),\n", + " (\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\", \"4d23fa6d\", datetime(2023, 2, 27), 0.2753, 0.9019, 1.4714, 1.6053, 0.0000, \"4d23fa6d\"),\n", + " (\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\", \"015f6874\", datetime(2023, 3, 1), 0.2817, 0.9160, 1.5027, 1.6109, 0.0000, \"015f6874\"),\n", + " (\"26f06d449da208ce64724b1463b07ad20746cbdc\", \"26f06d44\", datetime(2023, 3, 6), 0.2556, 0.9089, 1.4561, 1.5715, 0.0000, \"26f06d44\"),\n", + " (\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\", \"6b9d6bb8\", datetime(2023, 3, 6), 0.2790, 0.8965, 1.4669, 1.6271, 0.0000, \"6b9d6bb8\"),\n", + " (\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\", \"b796bd0f\", datetime(2023, 3, 6), 0.2512, 0.8887, 1.4229, 1.5932, 0.0000, \"b796bd0f\"),\n", + " (\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\", \"780aef7c\", datetime(2023, 3, 7), 0.2794, 0.9088, 1.4623, 1.6361, 0.0000, \"780aef7c\"),\n", + " (\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\", \"9f93ad9b\", datetime(2023, 3, 7), 0.2768, 0.9222, 1.4693, 1.5947, 0.0000, \"9f93ad9b\"),\n", + " (\"16628a0ba45a675df762245694e0a7666a3478f8\", \"16628a0b\", datetime(2023, 3, 7), 0.2711, 0.9046, 1.4684, 1.6177, 0.1972, \"v3.3\"),\n", + " (\"01684c8559604344bd09791268131819a09770a8\", \"01684c85\", datetime(2023, 3, 17), 0.2780, 0.8966, 1.4665, 1.6184, 0.0000, \"01684c85\"),\n", + " (\"e9231fb893c765b723fa4c1e087a58761b6aa471\", \"e9231fb8\", datetime(2023, 3, 20), 0.2766, 0.9115, 1.4520, 1.6247, 0.0000, \"e9231fb8\"),\n", + " (\"219889e243ffc69c71b6f7747f5af751d5694de1\", \"219889e2\", datetime(2023, 3, 23), 0.2789, 0.9155, 1.4392, 1.6172, 0.0000, \"219889e2\"),\n", + " (\"6124d2a82a7a823722210bc2e8516d355ba19eb3\", \"6124d2a8\", datetime(2023, 4, 5), 0.2793, 0.9102, 1.4421, 1.5822, 0.0000, \"6124d2a8\"),\n", + " (\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\", \"f6e4287f\", datetime(2023, 4, 25), 0.2811, 0.9127, 1.4950, 1.6169, 0.0000, \"f6e4287f\"),\n", + " (\"f2797fef396f2f19b02abb1f9555b678dac614f1\", \"f2797fef\", datetime(2023, 4, 25), 0.2821, 0.9147, 1.4608, 1.5963, 0.0000, \"f2797fef\"),\n", + " (\"b4e538f530048fec58eaca5170be82c67dbdcceb\", \"b4e538f5\", datetime(2023, 4, 25), 0.2819, 0.9356, 1.4703, 1.6130, 0.0000, \"b4e538f5\"),\n", + " (\"68820b715ed6b2c981aa11d29c0102e879280d79\", \"68820b71\", datetime(2023, 4, 25), 0.2761, 0.9253, 1.4697, 1.6078, 0.0000, \"68820b71\"),\n", + " (\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\", \"03deffed\", datetime(2023, 4, 25), 0.2842, 0.9258, 1.4871, 1.6294, 0.0000, \"03deffed\"),\n", + " (\"0d2bfecc271d561f67050659684b4797af8ee740\", \"0d2bfecc\", datetime(2023, 4, 25), 0.2778, 0.9204, 1.4512, 1.6204, 0.0000, \"0d2bfecc\"),\n", + " (\"1d03a465593f56c99a64a576d185d4ed17b659f2\", \"1d03a465\", datetime(2023, 4, 25), 0.2814, 0.8922, 1.4347, 1.6041, 0.0000, \"1d03a465\"),\n", + " (\"78a953b7ef9a36b62e5b446c80ed68abfddbfb74\", \"78a953b7\", datetime(2023, 5, 4), 0.2840, 0.9124, 1.4620, 1.6255, 0.0000, \"78a953b7\"),\n", + " (\"6c4f70ffbf3d4d2922d41d0032ae1b93d8a23c99\", \"6c4f70ff\", datetime(2023, 5, 4), 0.2833, 0.9212, 1.4702, 1.6108, 0.0000, \"6c4f70ff\"),\n", + " (\"ab03282623d0262b20b8c132efcdcace2dace766\", \"ab032826\", datetime(2023, 5, 6), 0.2568, 0.8974, 1.3970, 1.5902, 0.0000, \"ab032826\"),\n", + " (\"d2f7a45af27a6b40027d6f6a0f4f0be0c6dee5d9\", \"d2f7a45a\", datetime(2023, 5, 6), 0.2576, 0.8952, 1.4196, 1.5975, 0.0000, \"d2f7a45a\"),\n", + " (\"98b23f3d517481b127f190f5f8b7ebfae7f8b6b2\", \"98b23f3d\", datetime(2023, 5, 6), 0.2595, 0.8805, 1.4311, 1.5829, 0.0000, \"98b23f3d\"),\n", + " (\"452425de723cc1640d999022389672caf9bffbd0\", \"452425de\", datetime(2023, 5, 6), 0.2604, 0.9168, 1.4529, 1.5993, 0.0000, \"452425de\"),\n", + " (\"85dadb1a566c9fa8dc84cb9837b98bd5d23b8d58\", \"85dadb1a\", datetime(2023, 5, 7), 0.2607, 0.8738, 1.4423, 1.5903, 0.0000, \"85dadb1a\"),\n", + " (\"432ee7f96c1f6cccd05a0034c86c720cdb63a3e6\", \"432ee7f9\", datetime(2023, 5, 10), 0.2595, 0.8962, 1.4363, 1.6035, 0.0000, \"432ee7f9\"),\n", + " (\"ebd70ecaef14c0e239337eb6e36506303378a31a\", \"ebd70eca\", datetime(2023, 5, 10), 0.2589, 0.8722, 1.4290, 1.5817, 0.0000, \"ebd70eca\"),\n", + " (\"77fa7155d55bdf3fd43e29f58fe57feffcb107cf\", \"77fa7155\", datetime(2023, 5, 11), 0.2601, 0.8918, 1.4060, 1.6026, 0.0000, \"77fa7155\"),\n", + " (\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\", \"d5d4b134\", datetime(2023, 5, 12), 0.2618, 0.8922, 1.4582, 1.6016, 0.0000, \"d5d4b134\"),\n", + " (\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\", \"d5d4b134\", datetime(2023, 5, 16), 0.2601, 0.9014, 1.4262, 1.5990, 0.0000, \"d5d4b134\"),\n", + " (\"7c879f1ce18b52d9b0a8eecf877d03e66afc975b\", \"7c879f1c\", datetime(2023, 5, 16), 0.2556, 0.9021, 1.4220, 1.5603, 0.0000, \"7c879f1c\"),\n", + " (\"2aa9f2a55686f2ee5dc407e8e0223eb25176d906\", \"2aa9f2a5\", datetime(2023, 5, 16), 0.2565, 0.8991, 1.4399, 1.5938, 0.0000, \"2aa9f2a5\"),\n", + " (\"5e5bb7f4e653621e7a81ff4bcaa27dbc1f759de7\", \"5e5bb7f4\", datetime(2023, 5, 16), 0.2545, 0.9005, 1.4188, 1.5943, 0.0000, \"v3.4\"),\n", + " (\"d91953a499dfb88b457a1e7a07903debbda4058b\", \"d91953a4\", datetime(2023, 6, 1), 0.2572, 0.8675, 1.4323, 1.5862, 0.0000, \"d91953a4\"),\n", + " (\"76742879c81c9baced49b9fc60abbf1d2eba65ff\", \"76742879\", datetime(2023, 7, 3), 0.2558, 0.8890, 1.4395, 1.5833, 0.0000, \"76742879\"),\n", + " (\"9c73a41eaca95bb718ac79980a1799dfa1c48cf3\", \"9c73a41e\", datetime(2023, 7, 6), 0.2608, 0.8788, 1.4301, 1.5938, 0.0000, \"9c73a41e\"),\n", + " (\"67104dd714de939be136646af68edd9643ddfcd3\", \"67104dd7\", datetime(2023, 7, 6), 0.3009, 0.8573, 1.0494, 1.2918, 0.0000, \"67104dd7\"),\n", "]\n", "\n", "df = pd.DataFrame(data=data, columns=columns)\n", @@ -122,7 +142,7 @@ "data": { "text/html": [ "\n", - "
\n" + " \n" ] }, "metadata": {}, @@ -130,7 +150,7 @@ }, { "data": { - "application/javascript": "(function(root) {\n function embed_document(root) {\n const docs_json = {\"14a00e77-c0d8-428a-ada5-8c30bc2ee6af\":{\"defs\":[],\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1016\"}],\"center\":[{\"id\":\"1019\"},{\"id\":\"1023\"},{\"id\":\"1068\"}],\"height\":450,\"left\":[{\"id\":\"1020\"}],\"renderers\":[{\"id\":\"1044\"},{\"id\":\"1074\"},{\"id\":\"1102\"},{\"id\":\"1131\"},{\"id\":\"1159\"},{\"id\":\"1188\"},{\"id\":\"1216\"},{\"id\":\"1245\"}],\"title\":{\"id\":\"1006\"},\"toolbar\":{\"id\":\"1032\"},\"x_range\":{\"id\":\"1008\"},\"x_scale\":{\"id\":\"1012\"},\"y_range\":{\"id\":\"1010\"},\"y_scale\":{\"id\":\"1014\"}},\"id\":\"1005\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"data\":{\"cc\":{\"__ndarray__\":\"AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H9JLv8h/fb9P2gibHh6pf0/ZmZmZmZm/T+q8dJNYhD+P/7UeOkmMf4//yH99nXg/T/cRgN4CyT+P8BbIEHxY/0/tTf4wmSq/T8ydy0hH/T9P0oMAiuHFv4/INJvXwfO/T9q3nGKjuT9Px+F61G4Hv4/Di2yne+n/T+jI7n8h/T9P0Ck374OnP0/JXUCmggb/j/gnBGlvcH9PyegibDh6f0/0m9fB84Z/j9SuB6F61H+P7x0kxgEVv4/MlUwKqkT/j/l8h/Sb1/+PwMJih9j7v4/9bnaiv1l/j+DL0ymCkb+P2Rd3EYDeP4/3+ALk6mC/j8GgZVDi2z+P9NNYhBYOf4/uK8D54wo/T8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"code_coverage\":{\"__ndarray__\":\"PnlYqDXN2z/WVuwvuyfTPxB6Nqs+V9M/vJaQD3o21z8EVg4tsp3XP+PHmLuWkNc/lIeFWtO81z+Uh4Va07zXP9DVVuwvu9c/cvkP6bev1z/9h/Tb14HXP3sUrkfhetg/t2J/2T152D+QMXctIR/EP5Axdy0hH8Q/K/aX3ZOHxT+jkjoBTYTFP0T67evAOcM/ylTBqKROyD/8qfHSTWLIP662Yn/ZPck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACutmJ/2T3JPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"commit_hash\":[\"df25a9cfacd3d652361d2bd37f568af00acb2631\",\"b797390a43298a815f3ff57955cfdc71ecf3e866\",\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\",\"dd847210082035d43b0273ae63a76a53cb8d2e12\",\"33779269e98cc882a5f066c462d8ec1eadf37a1a\",\"12890e029a7155b074b9b325d320d1798338e287\",\"66dafc08bd620d96deda7d526b0e4bfc3b086650\",\"a325819b3b03b84bd76ad455e3f9b4600744ba14\",\"8a2c1a610295c007f0222ce737723c341189811d\",\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\",\"03e1f461c152e4f221fe92c834f2787680cf5772\",\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\",\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\",\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\",\"d18f4d263ecabf502242592f9d60815a07c7b89c\",\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\",\"c2006b0011a5df036c306c15e75763ec492dafda\",\"0c2adf3e702b6427da946a6ba9dbedbea22738be\",\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\",\"8436fd78b002e5792f5d0dd1409332d171036d49\",\"07a45b66c5facfea06c40bd82e34040c97560640\",\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\",\"4d528a3d6456621a382d409b5145a877b5414b88\",\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\",\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\",\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\",\"26f06d449da208ce64724b1463b07ad20746cbdc\",\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\",\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\",\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\",\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\",\"16628a0ba45a675df762245694e0a7666a3478f8\",\"01684c8559604344bd09791268131819a09770a8\",\"e9231fb893c765b723fa4c1e087a58761b6aa471\",\"219889e243ffc69c71b6f7747f5af751d5694de1\",\"6124d2a82a7a823722210bc2e8516d355ba19eb3\",\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\",\"f2797fef396f2f19b02abb1f9555b678dac614f1\",\"b4e538f530048fec58eaca5170be82c67dbdcceb\",\"68820b715ed6b2c981aa11d29c0102e879280d79\",\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\",\"0d2bfecc271d561f67050659684b4797af8ee740\",\"1d03a465593f56c99a64a576d185d4ed17b659f2\"],\"commit_hash_8char\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"03e1f461\",\"9e96d6c4\",\"2a98428f\",\"9b4e85cf\",\"d18f4d26\",\"a23241bb\",\"c2006b00\",\"0c2adf3e\",\"39c46600\",\"8436fd78\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"16628a0b\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\"],\"date\":{\"__ndarray__\":\"AACAyDfgd0IAAEDF0+F3QgAAACsm4ndCAACA9srid0IAAICNFOR3QgAAQPNm5HdCAAAAWbnkd0IAAMC+C+V3QgAAgCRe5XdCAACAJF7ld0IAAIC7p+Z3QgAAQCH65ndCAACA3Krvd0IAAABtLPR3QgAAQJ4j9XdCAAAAvML/d0IAAMBWixh4QgAAQIU5NHhCAAAAWyVJeEIAAAAn5WJ4QgAAACflYnhCAACAt2ZneEIAAEAduWd4QgAAQLQCaXhCAABAtAJpeEIAAMB/p2l4QgAAgHxDa3hCAACAfENreEIAAIB8Q2t4QgAAQOKVa3hCAABA4pVreEIAAEDilWt4QgAAwNvNbnhCAAAADcVveEIAAEA+vHB4QgAAAGnrdHhCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEI=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"gauss\":{\"__ndarray__\":\"GJXUCWgi9D+Hp1fKMsTzP240gLdAguw/845TdCSX6z8s1JrmHafsPyZTBaOSOu0/MCqpE9BE7D9hw9MrZRnsP9jw9EpZhuw/pN++Dpwz7D/VeOkmMQjsP57vp8ZLN+s/pgpGJXUC7D/YgXNGlPbuP9lfdk8eFu4/LSEf9GxW7z/HuriNBvDuPzSitDf4wu4/RpT2Bl+Y7j/wFkhQ/BjvPwpoImx4eu8/bHh6pSxD7j8rGJXUCWjuPwn5oGez6u8/4JwRpb3B7z8HzhlR2hvwP6UsQxzr4u4/SOF6FK5H7z+oxks3iUHuP6Fns+pzte8/w/UoXI/C7z8K16NwPQrvPw8LtaZ5x+8/UiegibDh7z+IY13cRgPwP5SHhVrTvO8/GQRWDi2y7z92cRsN4C3wPxWMSuoENO8/escpOpLL7z/IBz2bVZ/vP/kP6bevA/A/gZVDi2zn7z8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"gch\":{\"__ndarray__\":\"X5hMFYxK+j9IUPwYc9f8Px3J5T+k3/c/2T15WKg19z9seHqlLEP4Pxzr4jYawPg/lPYGX5hM+D9xrIvbaAD4P4V80LNZ9fg/io7k8h/S9z/ChqdXyjL4P4XrUbgehfc/KxiV1Alo+D8730+Nl275P7x0kxgEVvk/seHplbIM+j988rBQa5r5P3EbDeAtkPk/ylTBqKRO+T/6fmq8dJP5PzMzMzMzM/k/HHxhMlUw+T8g0m9fB874P+84RUdy+fg/GCZTBaOS+T8Cmggbnl75PxE2PL1Slvk/3GgAb4EE+j80ETY8vVL5P4MvTKYKRvk/rIvbaABv+T9O0ZFc/kP5P2sr9pfdk/k/ylTBqKRO+T97gy9Mpgr5PzXvOEVHcvk/8WPMXUvI+T+e76fGSzf5P/hT46WbxPk/YTJVMCqp+T9XW7G/7J75P6+UZYhjXfk/6+I2GsBb+T8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],\"jensen\":{\"__ndarray__\":\"WKg1zTtO9D/vOEVHcvnlP2PuWkI+6Nk/qmBUUieg2T/D9Shcj8LZP4EExY8xd9k/uB6F61G42j8/V1uxv+zaP1+YTBWMSto/bAn5oGez2j/swDkjSnvbP7u4jQbwFtw/ylTBqKRO0D9R2ht8YTLRP3gLJCh+jNE/irDh6ZWy0D+Sy39Iv33RP/mgZ7Pqc9E/gQTFjzF30T/mriXkg57RP5Cg+DHmrtE/TYQNT6+U0T+GWtO84xTRP1tCPujZrNI/FNBE2PD00j+JQWDl0CLTPwyTqYJRSdE/nDOitDf40j8mUwWjkjrRP99PjZduEtM/tMh2vp8a0z8qOpLLf0jTP3ZPHhZqTdM/RiV1ApoI0z9DrWnecYrSP/kP6bevA9M/sHJoke180z+dgCbChqfTP/RsVn2uttI/KjqSy39I0z+Nl24Sg8DSP5+rrdhfdtM/p3nHKTqS0z8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"tooltip_label\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"PR #56\",\"v3.0rc1\",\"PR #317\",\"v3.0\",\"v3.0.1\",\"v3.1\",\"v3.1.1\",\"v3.2\",\"v3.2.1\",\"v3.2.2\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"v3.3\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\"]},\"selected\":{\"id\":\"1054\"},\"selection_policy\":{\"id\":\"1053\"}},\"id\":\"1003\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"tools\":[{\"id\":\"1024\"},{\"id\":\"1025\"},{\"id\":\"1026\"},{\"id\":\"1027\"},{\"id\":\"1028\"},{\"id\":\"1029\"},{\"id\":\"1031\"}]},\"id\":\"1032\",\"type\":\"Toolbar\"},{\"attributes\":{\"fill_color\":{\"value\":\"blue\"},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1071\",\"type\":\"Circle\"},{\"attributes\":{\"bottom_units\":\"screen\",\"coordinates\":null,\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"group\":null,\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"syncable\":false,\"top_units\":\"screen\"},\"id\":\"1030\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1028\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"1027\",\"type\":\"SaveTool\"},{\"attributes\":{\"overlay\":{\"id\":\"1030\"}},\"id\":\"1026\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1242\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1244\"},\"nonselection_glyph\":{\"id\":\"1243\"},\"view\":{\"id\":\"1246\"}},\"id\":\"1245\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1024\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1025\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"cyan\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"cyan\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1244\",\"type\":\"Circle\"},{\"attributes\":{\"callback\":null,\"tooltips\":[[\"git ref\",\"@tooltip_label\"]]},\"id\":\"1031\",\"type\":\"HoverTool\"},{\"attributes\":{},\"id\":\"1021\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1020\"},\"coordinates\":null,\"dimension\":1,\"group\":null,\"ticker\":null},\"id\":\"1023\",\"type\":\"Grid\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1246\",\"type\":\"CDSView\"},{\"attributes\":{\"axis_label\":\"Time to solution (s)\",\"coordinates\":null,\"formatter\":{\"id\":\"1048\"},\"group\":null,\"major_label_policy\":{\"id\":\"1049\"},\"ticker\":{\"id\":\"1021\"}},\"id\":\"1020\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1029\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1014\",\"type\":\"LinearScale\"},{\"attributes\":{\"axis\":{\"id\":\"1016\"},\"coordinates\":null,\"group\":null,\"ticker\":null},\"id\":\"1019\",\"type\":\"Grid\"},{\"attributes\":{\"fill_color\":{\"value\":\"red\"},\"line_color\":{\"value\":\"red\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1185\",\"type\":\"Circle\"},{\"attributes\":{\"num_minor_ticks\":5,\"tickers\":[{\"id\":\"1056\"},{\"id\":\"1057\"},{\"id\":\"1058\"},{\"id\":\"1059\"},{\"id\":\"1060\"},{\"id\":\"1061\"},{\"id\":\"1062\"},{\"id\":\"1063\"},{\"id\":\"1064\"},{\"id\":\"1065\"},{\"id\":\"1066\"},{\"id\":\"1067\"}]},\"id\":\"1017\",\"type\":\"DatetimeTicker\"},{\"attributes\":{},\"id\":\"1054\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1053\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"cyan\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1214\",\"type\":\"Line\"},{\"attributes\":{\"label\":{\"value\":\"cc\"},\"renderers\":[{\"id\":\"1216\"},{\"id\":\"1245\"}]},\"id\":\"1240\",\"type\":\"LegendItem\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"cyan\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1215\",\"type\":\"Line\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"cyan\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"cyan\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1243\",\"type\":\"Circle\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1217\",\"type\":\"CDSView\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"red\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"red\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1187\",\"type\":\"Circle\"},{\"attributes\":{\"line_color\":\"cyan\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1213\",\"type\":\"Line\"},{\"attributes\":{\"fill_color\":{\"value\":\"cyan\"},\"line_color\":{\"value\":\"cyan\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1242\",\"type\":\"Circle\"},{\"attributes\":{\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1041\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1048\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1213\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1215\"},\"nonselection_glyph\":{\"id\":\"1214\"},\"view\":{\"id\":\"1217\"}},\"id\":\"1216\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"red\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1158\",\"type\":\"Line\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1043\",\"type\":\"Line\"},{\"attributes\":{\"border_line_color\":\"black\",\"click_policy\":\"mute\",\"coordinates\":null,\"group\":null,\"items\":[{\"id\":\"1069\"},{\"id\":\"1126\"},{\"id\":\"1183\"},{\"id\":\"1240\"}],\"location\":\"bottom_left\"},\"id\":\"1068\",\"type\":\"Legend\"},{\"attributes\":{\"label\":{\"value\":\"gch\"},\"renderers\":[{\"id\":\"1159\"},{\"id\":\"1188\"}]},\"id\":\"1183\",\"type\":\"LegendItem\"},{\"attributes\":{\"label\":{\"value\":\"jensen\"},\"renderers\":[{\"id\":\"1044\"},{\"id\":\"1074\"}]},\"id\":\"1069\",\"type\":\"LegendItem\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1042\",\"type\":\"Line\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1045\",\"type\":\"CDSView\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1132\",\"type\":\"CDSView\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1189\",\"type\":\"CDSView\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1041\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1043\"},\"nonselection_glyph\":{\"id\":\"1042\"},\"view\":{\"id\":\"1045\"}},\"id\":\"1044\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"green\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"green\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1130\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1010\",\"type\":\"DataRange1d\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"red\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1157\",\"type\":\"Line\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1128\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1130\"},\"nonselection_glyph\":{\"id\":\"1129\"},\"view\":{\"id\":\"1132\"}},\"id\":\"1131\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1008\",\"type\":\"DataRange1d\"},{\"attributes\":{\"line_color\":\"red\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1156\",\"type\":\"Line\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1185\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1187\"},\"nonselection_glyph\":{\"id\":\"1186\"},\"view\":{\"id\":\"1189\"}},\"id\":\"1188\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"red\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"red\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1186\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1012\",\"type\":\"LinearScale\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"text\":\"5x5 Wind Farm Timing Test\"},\"id\":\"1006\",\"type\":\"Title\"},{\"attributes\":{\"axis_label\":\"Commit date\",\"coordinates\":null,\"formatter\":{\"id\":\"1051\"},\"group\":null,\"major_label_policy\":{\"id\":\"1052\"},\"ticker\":{\"id\":\"1017\"}},\"id\":\"1016\",\"type\":\"DatetimeAxis\"},{\"attributes\":{},\"id\":\"1051\",\"type\":\"DatetimeTickFormatter\"},{\"attributes\":{},\"id\":\"1049\",\"type\":\"AllLabels\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1156\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1158\"},\"nonselection_glyph\":{\"id\":\"1157\"},\"view\":{\"id\":\"1160\"}},\"id\":\"1159\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1052\",\"type\":\"AllLabels\"},{\"attributes\":{\"mantissas\":[1,2,5],\"max_interval\":500.0,\"num_minor_ticks\":0},\"id\":\"1056\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"label\":{\"value\":\"gauss\"},\"renderers\":[{\"id\":\"1102\"},{\"id\":\"1131\"}]},\"id\":\"1126\",\"type\":\"LegendItem\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"green\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1101\",\"type\":\"Line\"},{\"attributes\":{\"days\":[1,8,15,22]},\"id\":\"1061\",\"type\":\"DaysTicker\"},{\"attributes\":{\"base\":60,\"mantissas\":[1,2,5,10,15,20,30],\"max_interval\":1800000.0,\"min_interval\":1000.0,\"num_minor_ticks\":0},\"id\":\"1057\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"green\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1100\",\"type\":\"Line\"},{\"attributes\":{\"base\":24,\"mantissas\":[1,2,4,6,8,12],\"max_interval\":43200000.0,\"min_interval\":3600000.0,\"num_minor_ticks\":0},\"id\":\"1058\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{},\"id\":\"1067\",\"type\":\"YearsTicker\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1160\",\"type\":\"CDSView\"},{\"attributes\":{\"days\":[1,4,7,10,13,16,19,22,25,28]},\"id\":\"1060\",\"type\":\"DaysTicker\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"green\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"green\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1129\",\"type\":\"Circle\"},{\"attributes\":{\"days\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]},\"id\":\"1059\",\"type\":\"DaysTicker\"},{\"attributes\":{\"months\":[0,6]},\"id\":\"1066\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"days\":[1,15]},\"id\":\"1062\",\"type\":\"DaysTicker\"},{\"attributes\":{\"months\":[0,1,2,3,4,5,6,7,8,9,10,11]},\"id\":\"1063\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"months\":[0,2,4,6,8,10]},\"id\":\"1064\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"months\":[0,4,8]},\"id\":\"1065\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1073\",\"type\":\"Circle\"},{\"attributes\":{\"fill_color\":{\"value\":\"green\"},\"line_color\":{\"value\":\"green\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1128\",\"type\":\"Circle\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1075\",\"type\":\"CDSView\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1103\",\"type\":\"CDSView\"},{\"attributes\":{\"line_color\":\"green\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1099\",\"type\":\"Line\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1071\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1073\"},\"nonselection_glyph\":{\"id\":\"1072\"},\"view\":{\"id\":\"1075\"}},\"id\":\"1074\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1099\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1101\"},\"nonselection_glyph\":{\"id\":\"1100\"},\"view\":{\"id\":\"1103\"}},\"id\":\"1102\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1072\",\"type\":\"Circle\"}],\"root_ids\":[\"1005\"]},\"title\":\"Bokeh Application\",\"version\":\"2.4.3\"}};\n const render_items = [{\"docid\":\"14a00e77-c0d8-428a-ada5-8c30bc2ee6af\",\"root_ids\":[\"1005\"],\"roots\":{\"1005\":\"b266d0f6-784c-4b82-810c-b68dc9aba108\"}}];\n root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n }\n if (root.Bokeh !== undefined) {\n embed_document(root);\n } else {\n let attempts = 0;\n const timer = setInterval(function(root) {\n if (root.Bokeh !== undefined) {\n clearInterval(timer);\n embed_document(root);\n } else {\n attempts++;\n if (attempts > 100) {\n clearInterval(timer);\n console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n }\n }\n }, 10, root)\n }\n})(window);", + "application/javascript": "(function(root) {\n function embed_document(root) {\n const docs_json = {\"38256105-4b1d-4911-ab8c-3ce49cec427b\":{\"defs\":[],\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1016\"}],\"center\":[{\"id\":\"1019\"},{\"id\":\"1023\"},{\"id\":\"1068\"}],\"height\":450,\"left\":[{\"id\":\"1020\"}],\"renderers\":[{\"id\":\"1044\"},{\"id\":\"1074\"},{\"id\":\"1102\"},{\"id\":\"1131\"},{\"id\":\"1159\"},{\"id\":\"1188\"},{\"id\":\"1216\"},{\"id\":\"1245\"}],\"title\":{\"id\":\"1006\"},\"toolbar\":{\"id\":\"1032\"},\"x_range\":{\"id\":\"1008\"},\"x_scale\":{\"id\":\"1012\"},\"y_range\":{\"id\":\"1010\"},\"y_scale\":{\"id\":\"1014\"}},\"id\":\"1005\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1132\",\"type\":\"CDSView\"},{\"attributes\":{\"fill_color\":{\"value\":\"green\"},\"line_color\":{\"value\":\"green\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1128\",\"type\":\"Circle\"},{\"attributes\":{\"label\":{\"value\":\"cc\"},\"renderers\":[{\"id\":\"1216\"},{\"id\":\"1245\"}]},\"id\":\"1240\",\"type\":\"LegendItem\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1075\",\"type\":\"CDSView\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"cyan\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1215\",\"type\":\"Line\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1073\",\"type\":\"Circle\"},{\"attributes\":{\"line_color\":\"green\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1099\",\"type\":\"Line\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1071\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1073\"},\"nonselection_glyph\":{\"id\":\"1072\"},\"view\":{\"id\":\"1075\"}},\"id\":\"1074\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"cyan\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"cyan\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1243\",\"type\":\"Circle\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1099\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1101\"},\"nonselection_glyph\":{\"id\":\"1100\"},\"view\":{\"id\":\"1103\"}},\"id\":\"1102\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1041\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1043\"},\"nonselection_glyph\":{\"id\":\"1042\"},\"view\":{\"id\":\"1045\"}},\"id\":\"1044\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1103\",\"type\":\"CDSView\"},{\"attributes\":{\"days\":[1,4,7,10,13,16,19,22,25,28]},\"id\":\"1060\",\"type\":\"DaysTicker\"},{\"attributes\":{\"label\":{\"value\":\"jensen\"},\"renderers\":[{\"id\":\"1044\"},{\"id\":\"1074\"}]},\"id\":\"1069\",\"type\":\"LegendItem\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"text\":\"5x5 Wind Farm Timing Test\"},\"id\":\"1006\",\"type\":\"Title\"},{\"attributes\":{\"bottom_units\":\"screen\",\"coordinates\":null,\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"group\":null,\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"syncable\":false,\"top_units\":\"screen\"},\"id\":\"1030\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"fill_color\":{\"value\":\"cyan\"},\"line_color\":{\"value\":\"cyan\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1242\",\"type\":\"Circle\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"green\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"green\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1130\",\"type\":\"Circle\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1072\",\"type\":\"Circle\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1043\",\"type\":\"Line\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1042\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1010\",\"type\":\"DataRange1d\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1213\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1215\"},\"nonselection_glyph\":{\"id\":\"1214\"},\"view\":{\"id\":\"1217\"}},\"id\":\"1216\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"red\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1157\",\"type\":\"Line\"},{\"attributes\":{\"fill_color\":{\"value\":\"red\"},\"line_color\":{\"value\":\"red\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1185\",\"type\":\"Circle\"},{\"attributes\":{\"data\":{\"cc\":{\"__ndarray__\":\"AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H+5jQbwFkj5P+f7qfHSTfk/6Ugu/yH9+D8c6+I2GsD5P5f/kH77Ovk/pN++Dpwz+T9t5/up8dL5P3lYqDXNO/k/u7iNBvAW+T+I9NvXgXP5Pz81XrpJDPo/rK3YX3ZP+T/XNO84RUf5P0aU9gZfmPk/AU2EDU+v+T+8BRIUP8b5P76fGi/dJPk/RiV1ApoI+j+Sy39Iv335PwXFjzF3Lfo/MuauJeSD+T/D0ytlGeL5P9uK/WX35Pk/rfpcbcX++T+OdXEbDeD5P/+ye/KwUPk/O3DOiNLe+T9DrWnecYr5PwIrhxbZzvk/CyQofoy5+T9uowG8BRL6P7AD54wo7fk/tTf4wmSq+T81XrpJDAL6P0tZhjjWxfk/U5YhjnVx+T+PwvUoXI/5PxZqTfOOU/k/guLHmLuW+T/EQq1p3nH5Pw4tsp3vp/k/ylTBqKRO+T8VHcnlP6T5P6pgVFInoPk/L90kBoGV+T9JLv8h/fb4PznWxW00gPk/bjSAt0CC+T+ppE5AE2H5P9obfGEyVfk/OdbFbTSA+T+XkA96Nqv0Pw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"code_coverage\":{\"__ndarray__\":\"PnlYqDXN2z/WVuwvuyfTPxB6Nqs+V9M/vJaQD3o21z8EVg4tsp3XP+PHmLuWkNc/lIeFWtO81z+Uh4Va07zXP9DVVuwvu9c/cvkP6bev1z/9h/Tb14HXP3sUrkfhetg/t2J/2T152D+QMXctIR/EP5Axdy0hH8Q/K/aX3ZOHxT+jkjoBTYTFP0T67evAOcM/ylTBqKROyD/8qfHSTWLIP662Yn/ZPck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACutmJ/2T3JPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"commit_hash\":[\"df25a9cfacd3d652361d2bd37f568af00acb2631\",\"b797390a43298a815f3ff57955cfdc71ecf3e866\",\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\",\"dd847210082035d43b0273ae63a76a53cb8d2e12\",\"33779269e98cc882a5f066c462d8ec1eadf37a1a\",\"12890e029a7155b074b9b325d320d1798338e287\",\"66dafc08bd620d96deda7d526b0e4bfc3b086650\",\"a325819b3b03b84bd76ad455e3f9b4600744ba14\",\"8a2c1a610295c007f0222ce737723c341189811d\",\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\",\"03e1f461c152e4f221fe92c834f2787680cf5772\",\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\",\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\",\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\",\"d18f4d263ecabf502242592f9d60815a07c7b89c\",\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\",\"c2006b0011a5df036c306c15e75763ec492dafda\",\"0c2adf3e702b6427da946a6ba9dbedbea22738be\",\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\",\"8436fd78b002e5792f5d0dd1409332d171036d49\",\"07a45b66c5facfea06c40bd82e34040c97560640\",\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\",\"4d528a3d6456621a382d409b5145a877b5414b88\",\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\",\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\",\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\",\"26f06d449da208ce64724b1463b07ad20746cbdc\",\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\",\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\",\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\",\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\",\"16628a0ba45a675df762245694e0a7666a3478f8\",\"01684c8559604344bd09791268131819a09770a8\",\"e9231fb893c765b723fa4c1e087a58761b6aa471\",\"219889e243ffc69c71b6f7747f5af751d5694de1\",\"6124d2a82a7a823722210bc2e8516d355ba19eb3\",\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\",\"f2797fef396f2f19b02abb1f9555b678dac614f1\",\"b4e538f530048fec58eaca5170be82c67dbdcceb\",\"68820b715ed6b2c981aa11d29c0102e879280d79\",\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\",\"0d2bfecc271d561f67050659684b4797af8ee740\",\"1d03a465593f56c99a64a576d185d4ed17b659f2\",\"78a953b7ef9a36b62e5b446c80ed68abfddbfb74\",\"6c4f70ffbf3d4d2922d41d0032ae1b93d8a23c99\",\"ab03282623d0262b20b8c132efcdcace2dace766\",\"d2f7a45af27a6b40027d6f6a0f4f0be0c6dee5d9\",\"98b23f3d517481b127f190f5f8b7ebfae7f8b6b2\",\"452425de723cc1640d999022389672caf9bffbd0\",\"85dadb1a566c9fa8dc84cb9837b98bd5d23b8d58\",\"432ee7f96c1f6cccd05a0034c86c720cdb63a3e6\",\"ebd70ecaef14c0e239337eb6e36506303378a31a\",\"77fa7155d55bdf3fd43e29f58fe57feffcb107cf\",\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\",\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\",\"7c879f1ce18b52d9b0a8eecf877d03e66afc975b\",\"2aa9f2a55686f2ee5dc407e8e0223eb25176d906\",\"5e5bb7f4e653621e7a81ff4bcaa27dbc1f759de7\",\"d91953a499dfb88b457a1e7a07903debbda4058b\",\"76742879c81c9baced49b9fc60abbf1d2eba65ff\",\"9c73a41eaca95bb718ac79980a1799dfa1c48cf3\",\"67104dd714de939be136646af68edd9643ddfcd3\"],\"commit_hash_8char\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"03e1f461\",\"9e96d6c4\",\"2a98428f\",\"9b4e85cf\",\"d18f4d26\",\"a23241bb\",\"c2006b00\",\"0c2adf3e\",\"39c46600\",\"8436fd78\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"16628a0b\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\",\"78a953b7\",\"6c4f70ff\",\"ab032826\",\"d2f7a45a\",\"98b23f3d\",\"452425de\",\"85dadb1a\",\"432ee7f9\",\"ebd70eca\",\"77fa7155\",\"d5d4b134\",\"d5d4b134\",\"7c879f1c\",\"2aa9f2a5\",\"5e5bb7f4\",\"d91953a4\",\"76742879\",\"9c73a41e\",\"67104dd7\"],\"date\":{\"__ndarray__\":\"AACAyDfgd0IAAEDF0+F3QgAAACsm4ndCAACA9srid0IAAICNFOR3QgAAQPNm5HdCAAAAWbnkd0IAAMC+C+V3QgAAgCRe5XdCAACAJF7ld0IAAIC7p+Z3QgAAQCH65ndCAACA3Krvd0IAAABtLPR3QgAAQJ4j9XdCAAAAvML/d0IAAMBWixh4QgAAQIU5NHhCAAAAWyVJeEIAAAAn5WJ4QgAAACflYnhCAACAt2ZneEIAAEAduWd4QgAAQLQCaXhCAABAtAJpeEIAAMB/p2l4QgAAgHxDa3hCAACAfENreEIAAIB8Q2t4QgAAQOKVa3hCAABA4pVreEIAAEDilWt4QgAAwNvNbnhCAAAADcVveEIAAEA+vHB4QgAAAGnrdHhCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAMDvQH54QgAAwO9AfnhCAABAu+V+eEIAAEC75X54QgAAQLvlfnhCAABAu+V+eEIAAAAhOH94QgAAQFIvgHhCAABAUi+AeEIAAAC4gYB4QgAAwB3UgHhCAADAtB2CeEIAAMC0HYJ4QgAAwLQdgnhCAADAtB2CeEIAAMAQRId4QgAAwMiQkXhCAAAA+oeSeEIAAAD6h5J4Qg==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"gauss\":{\"__ndarray__\":\"GJXUCWgi9D+Hp1fKMsTzP0Jg5dAi2+k/xLEubqMB6j/LEMe6uI3qP9CzWfW52uo/1zTvOEVH6j/+1HjpJjHqPzhnRGlv8Ok/FR3J5T+k6T/MXUvIBz3pP99PjZduEuk/kst/SL996T/129eBc0bsPwmKH2PuWuw/P8bctYR87D+IY13cRgPsPw1xrIvbaOw/0m9fB84Z7T+94xQdyeXrP7snDwu1pus/r5RliGNd7D9sCfmgZ7PsP8UgsHJoke0/lWWIY13c7D8dWmQ730/tP2iz6nO1Fe0/46WbxCCw7D8AkX77OnDsP4Za07zjFO0/3+ALk6mC7T9txf6ye/LsP8X+snvysOw/XrpJDAIr7T+yne+nxkvtP+M2GsBbIO0/9+RhodY07T+h1jTvOEXtPzhnRGlv8O0/QKTfvg6c7T+qYFRSJ6DtP/mgZ7Pqc+0/6bevA+eM7D9R2ht8YTLtPwpoImx4eu0/1sVtNIC37D9oImx4eqXsP5MYBFYOLew/LSEf9GxW7T9n1edqK/brPz2bVZ+rrew/RUdy+Q/p6z9hVFInoInsP+m3rwPnjOw/KqkT0ETY7D9ZF7fRAN7sP9qs+lxtxew/N4lBYOXQ7D/D9Shcj8LrP6abxCCwcuw/kDF3LSEf7D+si9toAG/rPw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"gch\":{\"__ndarray__\":\"X5hMFYxK+j9IUPwYc9f8P1UwKqkT0PU/BoGVQ4ts9T+gibDh6ZX2P+M2GsBbIPY/FYxK6gQ09j8OvjCZKhj2P9cS8kHPZvY/5WGh1jTv9j/LEMe6uI32P2q8dJMYBPY/f/s6cM6I9T8MAiuHFtn2P807TtGRXPc/ofgx5q4l9z+NKO0NvjD3P8rDQq1p3vY/bHh6pSxD9z/dJAaBlUP3P8dLN4lBYPc/63O1FfvL9j9UdCSX/5D2PzvfT42Xbvc/tFn1udqK9z/sL7snDwv4PyNKe4MvTPc/1QloImx49z+Hp1fKMsT2P4QNT6+UZfc/bjSAt0CC9z90JJf/kH73PxBYObTIdvc/CKwcWmQ79z+Cc0aU9gb3P1D8GHPXEvc/7FG4HoXr9z/l8h/Sb1/3P9jw9EpZhvc/MuauJeSD9z8JG55eKcv3P4BIv30dOPc/oyO5/If09j8xCKwcWmT3P2dEaW/whfc/JzEIrBxa9j/0bFZ9rrb2P73jFB3J5fY/ArwFEhQ/9z8yVTAqqRP3P7Pqc7UV+/Y/d76fGi/d9j/l0CLb+X72P2lv8IXJVPc/GeJYF7fR9j+Nl24Sg8D2P5kqGJXUCfc/bAn5oGez9j8J+aBns+r2P9V46SYxCPc/UiegibDh9j8nwoanV8rwPw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61],\"jensen\":{\"__ndarray__\":\"WKg1zTtO9D/vOEVHcvnlP2EyVTAqqdc/fdCzWfW51j8f9GxWfa7WP9UJaCJseNY/r5RliGNd2D8awFsgQfHXP0Ck374OnNc/5fIf0m9f1z988rBQa5rXP6+UZYhjXdg/eVioNc07zj/Q1VbsL7vPP4y5awn5oM8/XI/C9Shczz9d3EYDeAvQP2WqYFRSJ9A/GJXUCWgi0D+coiO5/IfQP4qO5PIf0s8/f2q8dJMY0D+vlGWIY13QPzqSy39Iv9E/5q4l5IOe0T/zH9JvXwfSP+viNhrAW9A/QmDl0CLb0T8yVTAqqRPQP1InoImw4dE/ZRniWBe30T+2hHzQs1nRP5huEoPAytE/3bWEfNCz0T99rrZif9nRP451cRsN4NE/WvW52or90T8E54wo7Q3SP3uDL0ymCtI/CD2bVZ+r0T8cfGEyVTDSPw8LtaZ5x9E/pgpGJXUC0j+TGARWDi3SPzY8vVKWIdI/HThnRGlv0D8/xty1hHzQP8/3U+Olm9A/tTf4wmSq0D8BTYQNT6/QP8/3U+Olm9A/Ns07TtGR0D9oImx4eqXQP2/whclUwdA/aCJseHql0D/r4jYawFvQP9Ei2/l+atA/fT81XrpJ0D8u/yH99nXQP3RGlPYGX9A/xf6ye/Kw0D8Zc9cS8kHTPw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"tooltip_label\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"PR #56\",\"v3.0rc1\",\"PR #317\",\"v3.0\",\"v3.0.1\",\"v3.1\",\"v3.1.1\",\"v3.2\",\"v3.2.1\",\"v3.2.2\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"v3.3\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\",\"78a953b7\",\"6c4f70ff\",\"ab032826\",\"d2f7a45a\",\"98b23f3d\",\"452425de\",\"85dadb1a\",\"432ee7f9\",\"ebd70eca\",\"77fa7155\",\"d5d4b134\",\"d5d4b134\",\"7c879f1c\",\"2aa9f2a5\",\"v3.4\",\"d91953a4\",\"76742879\",\"9c73a41e\",\"67104dd7\"]},\"selected\":{\"id\":\"1054\"},\"selection_policy\":{\"id\":\"1053\"}},\"id\":\"1003\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1054\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1053\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1041\",\"type\":\"Line\"},{\"attributes\":{\"fill_color\":{\"value\":\"blue\"},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"jensen\"}},\"id\":\"1071\",\"type\":\"Circle\"},{\"attributes\":{\"line_color\":\"red\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1156\",\"type\":\"Line\"},{\"attributes\":{\"days\":[1,8,15,22]},\"id\":\"1061\",\"type\":\"DaysTicker\"},{\"attributes\":{\"months\":[0,4,8]},\"id\":\"1065\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"days\":[1,15]},\"id\":\"1062\",\"type\":\"DaysTicker\"},{\"attributes\":{},\"id\":\"1008\",\"type\":\"DataRange1d\"},{\"attributes\":{\"base\":24,\"mantissas\":[1,2,4,6,8,12],\"max_interval\":43200000.0,\"min_interval\":3600000.0,\"num_minor_ticks\":0},\"id\":\"1058\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{},\"id\":\"1014\",\"type\":\"LinearScale\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"cyan\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"cyan\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1244\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1012\",\"type\":\"LinearScale\"},{\"attributes\":{\"months\":[0,1,2,3,4,5,6,7,8,9,10,11]},\"id\":\"1063\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1128\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1130\"},\"nonselection_glyph\":{\"id\":\"1129\"},\"view\":{\"id\":\"1132\"}},\"id\":\"1131\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"axis_label\":\"Commit date\",\"coordinates\":null,\"formatter\":{\"id\":\"1051\"},\"group\":null,\"major_label_policy\":{\"id\":\"1052\"},\"ticker\":{\"id\":\"1017\"}},\"id\":\"1016\",\"type\":\"DatetimeAxis\"},{\"attributes\":{\"num_minor_ticks\":5,\"tickers\":[{\"id\":\"1056\"},{\"id\":\"1057\"},{\"id\":\"1058\"},{\"id\":\"1059\"},{\"id\":\"1060\"},{\"id\":\"1061\"},{\"id\":\"1062\"},{\"id\":\"1063\"},{\"id\":\"1064\"},{\"id\":\"1065\"},{\"id\":\"1066\"},{\"id\":\"1067\"}]},\"id\":\"1017\",\"type\":\"DatetimeTicker\"},{\"attributes\":{\"days\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]},\"id\":\"1059\",\"type\":\"DaysTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1016\"},\"coordinates\":null,\"group\":null,\"ticker\":null},\"id\":\"1019\",\"type\":\"Grid\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1156\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1158\"},\"nonselection_glyph\":{\"id\":\"1157\"},\"view\":{\"id\":\"1160\"}},\"id\":\"1159\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1160\",\"type\":\"CDSView\"},{\"attributes\":{\"base\":60,\"mantissas\":[1,2,5,10,15,20,30],\"max_interval\":1800000.0,\"min_interval\":1000.0,\"num_minor_ticks\":0},\"id\":\"1057\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"mantissas\":[1,2,5],\"max_interval\":500.0,\"num_minor_ticks\":0},\"id\":\"1056\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{},\"id\":\"1049\",\"type\":\"AllLabels\"},{\"attributes\":{},\"id\":\"1067\",\"type\":\"YearsTicker\"},{\"attributes\":{\"months\":[0,6]},\"id\":\"1066\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1045\",\"type\":\"CDSView\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"green\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1101\",\"type\":\"Line\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"green\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1100\",\"type\":\"Line\"},{\"attributes\":{\"label\":{\"value\":\"gauss\"},\"renderers\":[{\"id\":\"1102\"},{\"id\":\"1131\"}]},\"id\":\"1126\",\"type\":\"LegendItem\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"green\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"green\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gauss\"}},\"id\":\"1129\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1051\",\"type\":\"DatetimeTickFormatter\"},{\"attributes\":{},\"id\":\"1052\",\"type\":\"AllLabels\"},{\"attributes\":{},\"id\":\"1048\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"axis_label\":\"Time to solution (s)\",\"coordinates\":null,\"formatter\":{\"id\":\"1048\"},\"group\":null,\"major_label_policy\":{\"id\":\"1049\"},\"ticker\":{\"id\":\"1021\"}},\"id\":\"1020\",\"type\":\"LinearAxis\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1246\",\"type\":\"CDSView\"},{\"attributes\":{\"axis\":{\"id\":\"1020\"},\"coordinates\":null,\"dimension\":1,\"group\":null,\"ticker\":null},\"id\":\"1023\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1021\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1028\",\"type\":\"ResetTool\"},{\"attributes\":{\"callback\":null,\"tooltips\":[[\"git ref\",\"@tooltip_label\"]]},\"id\":\"1031\",\"type\":\"HoverTool\"},{\"attributes\":{},\"id\":\"1029\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1025\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1024\",\"type\":\"PanTool\"},{\"attributes\":{\"overlay\":{\"id\":\"1030\"}},\"id\":\"1026\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1027\",\"type\":\"SaveTool\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"red\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"red\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1186\",\"type\":\"Circle\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1185\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1187\"},\"nonselection_glyph\":{\"id\":\"1186\"},\"view\":{\"id\":\"1189\"}},\"id\":\"1188\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1242\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1244\"},\"nonselection_glyph\":{\"id\":\"1243\"},\"view\":{\"id\":\"1246\"}},\"id\":\"1245\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"line_color\":\"cyan\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1213\",\"type\":\"Line\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1189\",\"type\":\"CDSView\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"red\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"red\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1187\",\"type\":\"Circle\"},{\"attributes\":{\"months\":[0,2,4,6,8,10]},\"id\":\"1064\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"tools\":[{\"id\":\"1024\"},{\"id\":\"1025\"},{\"id\":\"1026\"},{\"id\":\"1027\"},{\"id\":\"1028\"},{\"id\":\"1029\"},{\"id\":\"1031\"}]},\"id\":\"1032\",\"type\":\"Toolbar\"},{\"attributes\":{\"label\":{\"value\":\"gch\"},\"renderers\":[{\"id\":\"1159\"},{\"id\":\"1188\"}]},\"id\":\"1183\",\"type\":\"LegendItem\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"red\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"gch\"}},\"id\":\"1158\",\"type\":\"Line\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1217\",\"type\":\"CDSView\"},{\"attributes\":{\"border_line_color\":\"black\",\"click_policy\":\"mute\",\"coordinates\":null,\"group\":null,\"items\":[{\"id\":\"1069\"},{\"id\":\"1126\"},{\"id\":\"1183\"},{\"id\":\"1240\"}],\"location\":\"bottom_left\"},\"id\":\"1068\",\"type\":\"Legend\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"cyan\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"cc\"}},\"id\":\"1214\",\"type\":\"Line\"}],\"root_ids\":[\"1005\"]},\"title\":\"Bokeh Application\",\"version\":\"2.4.3\"}};\n const render_items = [{\"docid\":\"38256105-4b1d-4911-ab8c-3ce49cec427b\",\"root_ids\":[\"1005\"],\"roots\":{\"1005\":\"730e09d0-024d-4350-a23d-4cf3dee3d66c\"}}];\n root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n }\n if (root.Bokeh !== undefined) {\n embed_document(root);\n } else {\n let attempts = 0;\n const timer = setInterval(function(root) {\n if (root.Bokeh !== undefined) {\n clearInterval(timer);\n embed_document(root);\n } else {\n attempts++;\n if (attempts > 100) {\n clearInterval(timer);\n console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n }\n }\n }, 10, root)\n }\n})(window);", "application/vnd.bokehjs_exec.v0+json": "" }, "metadata": { @@ -197,7 +217,7 @@ "data": { "text/html": [ "\n", - " \n" + " \n" ] }, "metadata": {}, @@ -205,7 +225,7 @@ }, { "data": { - "application/javascript": "(function(root) {\n function embed_document(root) {\n const docs_json = {\"77c55f8b-56eb-4d43-b824-70ae6cecde50\":{\"defs\":[],\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1412\"}],\"center\":[{\"id\":\"1415\"},{\"id\":\"1419\"}],\"height\":450,\"left\":[{\"id\":\"1416\"}],\"renderers\":[{\"id\":\"1440\"},{\"id\":\"1446\"}],\"title\":{\"id\":\"1402\"},\"toolbar\":{\"id\":\"1428\"},\"x_range\":{\"id\":\"1404\"},\"x_scale\":{\"id\":\"1408\"},\"y_range\":{\"id\":\"1448\"},\"y_scale\":{\"id\":\"1410\"}},\"id\":\"1401\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1492\",\"type\":\"YearsTicker\"},{\"attributes\":{\"data\":{\"cc\":{\"__ndarray__\":\"AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H9JLv8h/fb9P2gibHh6pf0/ZmZmZmZm/T+q8dJNYhD+P/7UeOkmMf4//yH99nXg/T/cRgN4CyT+P8BbIEHxY/0/tTf4wmSq/T8ydy0hH/T9P0oMAiuHFv4/INJvXwfO/T9q3nGKjuT9Px+F61G4Hv4/Di2yne+n/T+jI7n8h/T9P0Ck374OnP0/JXUCmggb/j/gnBGlvcH9PyegibDh6f0/0m9fB84Z/j9SuB6F61H+P7x0kxgEVv4/MlUwKqkT/j/l8h/Sb1/+PwMJih9j7v4/9bnaiv1l/j+DL0ymCkb+P2Rd3EYDeP4/3+ALk6mC/j8GgZVDi2z+P9NNYhBYOf4/uK8D54wo/T8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"code_coverage\":{\"__ndarray__\":\"PnlYqDXN2z/WVuwvuyfTPxB6Nqs+V9M/vJaQD3o21z8EVg4tsp3XP+PHmLuWkNc/lIeFWtO81z+Uh4Va07zXP9DVVuwvu9c/cvkP6bev1z/9h/Tb14HXP3sUrkfhetg/t2J/2T152D+QMXctIR/EP5Axdy0hH8Q/K/aX3ZOHxT+jkjoBTYTFP0T67evAOcM/ylTBqKROyD/8qfHSTWLIP662Yn/ZPck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACutmJ/2T3JPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"commit_hash\":[\"df25a9cfacd3d652361d2bd37f568af00acb2631\",\"b797390a43298a815f3ff57955cfdc71ecf3e866\",\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\",\"dd847210082035d43b0273ae63a76a53cb8d2e12\",\"33779269e98cc882a5f066c462d8ec1eadf37a1a\",\"12890e029a7155b074b9b325d320d1798338e287\",\"66dafc08bd620d96deda7d526b0e4bfc3b086650\",\"a325819b3b03b84bd76ad455e3f9b4600744ba14\",\"8a2c1a610295c007f0222ce737723c341189811d\",\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\",\"03e1f461c152e4f221fe92c834f2787680cf5772\",\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\",\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\",\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\",\"d18f4d263ecabf502242592f9d60815a07c7b89c\",\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\",\"c2006b0011a5df036c306c15e75763ec492dafda\",\"0c2adf3e702b6427da946a6ba9dbedbea22738be\",\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\",\"8436fd78b002e5792f5d0dd1409332d171036d49\",\"07a45b66c5facfea06c40bd82e34040c97560640\",\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\",\"4d528a3d6456621a382d409b5145a877b5414b88\",\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\",\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\",\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\",\"26f06d449da208ce64724b1463b07ad20746cbdc\",\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\",\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\",\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\",\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\",\"16628a0ba45a675df762245694e0a7666a3478f8\",\"01684c8559604344bd09791268131819a09770a8\",\"e9231fb893c765b723fa4c1e087a58761b6aa471\",\"219889e243ffc69c71b6f7747f5af751d5694de1\",\"6124d2a82a7a823722210bc2e8516d355ba19eb3\",\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\",\"f2797fef396f2f19b02abb1f9555b678dac614f1\",\"b4e538f530048fec58eaca5170be82c67dbdcceb\",\"68820b715ed6b2c981aa11d29c0102e879280d79\",\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\",\"0d2bfecc271d561f67050659684b4797af8ee740\",\"1d03a465593f56c99a64a576d185d4ed17b659f2\"],\"commit_hash_8char\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"03e1f461\",\"9e96d6c4\",\"2a98428f\",\"9b4e85cf\",\"d18f4d26\",\"a23241bb\",\"c2006b00\",\"0c2adf3e\",\"39c46600\",\"8436fd78\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"16628a0b\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\"],\"date\":{\"__ndarray__\":\"AACAyDfgd0IAAEDF0+F3QgAAACsm4ndCAACA9srid0IAAICNFOR3QgAAQPNm5HdCAAAAWbnkd0IAAMC+C+V3QgAAgCRe5XdCAACAJF7ld0IAAIC7p+Z3QgAAQCH65ndCAACA3Krvd0IAAABtLPR3QgAAQJ4j9XdCAAAAvML/d0IAAMBWixh4QgAAQIU5NHhCAAAAWyVJeEIAAAAn5WJ4QgAAACflYnhCAACAt2ZneEIAAEAduWd4QgAAQLQCaXhCAABAtAJpeEIAAMB/p2l4QgAAgHxDa3hCAACAfENreEIAAIB8Q2t4QgAAQOKVa3hCAABA4pVreEIAAEDilWt4QgAAwNvNbnhCAAAADcVveEIAAEA+vHB4QgAAAGnrdHhCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEI=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"gauss\":{\"__ndarray__\":\"GJXUCWgi9D+Hp1fKMsTzP240gLdAguw/845TdCSX6z8s1JrmHafsPyZTBaOSOu0/MCqpE9BE7D9hw9MrZRnsP9jw9EpZhuw/pN++Dpwz7D/VeOkmMQjsP57vp8ZLN+s/pgpGJXUC7D/YgXNGlPbuP9lfdk8eFu4/LSEf9GxW7z/HuriNBvDuPzSitDf4wu4/RpT2Bl+Y7j/wFkhQ/BjvPwpoImx4eu8/bHh6pSxD7j8rGJXUCWjuPwn5oGez6u8/4JwRpb3B7z8HzhlR2hvwP6UsQxzr4u4/SOF6FK5H7z+oxks3iUHuP6Fns+pzte8/w/UoXI/C7z8K16NwPQrvPw8LtaZ5x+8/UiegibDh7z+IY13cRgPwP5SHhVrTvO8/GQRWDi2y7z92cRsN4C3wPxWMSuoENO8/escpOpLL7z/IBz2bVZ/vP/kP6bevA/A/gZVDi2zn7z8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"gch\":{\"__ndarray__\":\"X5hMFYxK+j9IUPwYc9f8Px3J5T+k3/c/2T15WKg19z9seHqlLEP4Pxzr4jYawPg/lPYGX5hM+D9xrIvbaAD4P4V80LNZ9fg/io7k8h/S9z/ChqdXyjL4P4XrUbgehfc/KxiV1Alo+D8730+Nl275P7x0kxgEVvk/seHplbIM+j988rBQa5r5P3EbDeAtkPk/ylTBqKRO+T/6fmq8dJP5PzMzMzMzM/k/HHxhMlUw+T8g0m9fB874P+84RUdy+fg/GCZTBaOS+T8Cmggbnl75PxE2PL1Slvk/3GgAb4EE+j80ETY8vVL5P4MvTKYKRvk/rIvbaABv+T9O0ZFc/kP5P2sr9pfdk/k/ylTBqKRO+T97gy9Mpgr5PzXvOEVHcvk/8WPMXUvI+T+e76fGSzf5P/hT46WbxPk/YTJVMCqp+T9XW7G/7J75P6+UZYhjXfk/6+I2GsBb+T8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],\"jensen\":{\"__ndarray__\":\"WKg1zTtO9D/vOEVHcvnlP2PuWkI+6Nk/qmBUUieg2T/D9Shcj8LZP4EExY8xd9k/uB6F61G42j8/V1uxv+zaP1+YTBWMSto/bAn5oGez2j/swDkjSnvbP7u4jQbwFtw/ylTBqKRO0D9R2ht8YTLRP3gLJCh+jNE/irDh6ZWy0D+Sy39Iv33RP/mgZ7Pqc9E/gQTFjzF30T/mriXkg57RP5Cg+DHmrtE/TYQNT6+U0T+GWtO84xTRP1tCPujZrNI/FNBE2PD00j+JQWDl0CLTPwyTqYJRSdE/nDOitDf40j8mUwWjkjrRP99PjZduEtM/tMh2vp8a0z8qOpLLf0jTP3ZPHhZqTdM/RiV1ApoI0z9DrWnecYrSP/kP6bevA9M/sHJoke180z+dgCbChqfTP/RsVn2uttI/KjqSy39I0z+Nl24Sg8DSP5+rrdhfdtM/p3nHKTqS0z8=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[43]},\"tooltip_label\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"PR #56\",\"v3.0rc1\",\"PR #317\",\"v3.0\",\"v3.0.1\",\"v3.1\",\"v3.1.1\",\"v3.2\",\"v3.2.1\",\"v3.2.2\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"v3.3\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\"]},\"selected\":{\"id\":\"1054\"},\"selection_policy\":{\"id\":\"1053\"}},\"id\":\"1003\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1421\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"base\":60,\"mantissas\":[1,2,5,10,15,20,30],\"max_interval\":1800000.0,\"min_interval\":1000.0,\"num_minor_ticks\":0},\"id\":\"1482\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"text\":\"Code Coverage\"},\"id\":\"1402\",\"type\":\"Title\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1445\",\"type\":\"Circle\"},{\"attributes\":{\"days\":[1,8,15,22]},\"id\":\"1486\",\"type\":\"DaysTicker\"},{\"attributes\":{\"callback\":null,\"tooltips\":[[\"git ref\",\"@tooltip_label\"]]},\"id\":\"1427\",\"type\":\"HoverTool\"},{\"attributes\":{\"tools\":[{\"id\":\"1420\"},{\"id\":\"1421\"},{\"id\":\"1422\"},{\"id\":\"1423\"},{\"id\":\"1424\"},{\"id\":\"1425\"},{\"id\":\"1427\"}]},\"id\":\"1428\",\"type\":\"Toolbar\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1443\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1445\"},\"nonselection_glyph\":{\"id\":\"1444\"},\"view\":{\"id\":\"1447\"}},\"id\":\"1446\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"overlay\":{\"id\":\"1426\"}},\"id\":\"1422\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"months\":[0,2,4,6,8,10]},\"id\":\"1489\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"bottom_units\":\"screen\",\"coordinates\":null,\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"group\":null,\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"syncable\":false,\"top_units\":\"screen\"},\"id\":\"1426\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"axis_label\":\"Test coverage as a percentage of Python code\",\"coordinates\":null,\"formatter\":{\"id\":\"1473\"},\"group\":null,\"major_label_policy\":{\"id\":\"1474\"},\"ticker\":{\"id\":\"1417\"}},\"id\":\"1416\",\"type\":\"LinearAxis\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1441\",\"type\":\"CDSView\"},{\"attributes\":{\"axis\":{\"id\":\"1416\"},\"coordinates\":null,\"dimension\":1,\"group\":null,\"ticker\":null},\"id\":\"1419\",\"type\":\"Grid\"},{\"attributes\":{\"months\":[0,6]},\"id\":\"1491\",\"type\":\"MonthsTicker\"},{\"attributes\":{},\"id\":\"1425\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1476\",\"type\":\"DatetimeTickFormatter\"},{\"attributes\":{\"days\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]},\"id\":\"1484\",\"type\":\"DaysTicker\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1438\",\"type\":\"Line\"},{\"attributes\":{\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1437\",\"type\":\"Line\"},{\"attributes\":{\"months\":[0,4,8]},\"id\":\"1490\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"months\":[0,1,2,3,4,5,6,7,8,9,10,11]},\"id\":\"1488\",\"type\":\"MonthsTicker\"},{\"attributes\":{},\"id\":\"1417\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1448\",\"type\":\"Range1d\"},{\"attributes\":{\"mantissas\":[1,2,5],\"max_interval\":500.0,\"num_minor_ticks\":0},\"id\":\"1481\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1444\",\"type\":\"Circle\"},{\"attributes\":{\"base\":24,\"mantissas\":[1,2,4,6,8,12],\"max_interval\":43200000.0,\"min_interval\":3600000.0,\"num_minor_ticks\":0},\"id\":\"1483\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"days\":[1,4,7,10,13,16,19,22,25,28]},\"id\":\"1485\",\"type\":\"DaysTicker\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1447\",\"type\":\"CDSView\"},{\"attributes\":{\"days\":[1,15]},\"id\":\"1487\",\"type\":\"DaysTicker\"},{\"attributes\":{\"num_minor_ticks\":5,\"tickers\":[{\"id\":\"1481\"},{\"id\":\"1482\"},{\"id\":\"1483\"},{\"id\":\"1484\"},{\"id\":\"1485\"},{\"id\":\"1486\"},{\"id\":\"1487\"},{\"id\":\"1488\"},{\"id\":\"1489\"},{\"id\":\"1490\"},{\"id\":\"1491\"},{\"id\":\"1492\"}]},\"id\":\"1413\",\"type\":\"DatetimeTicker\"},{\"attributes\":{\"axis_label\":\"Commit date\",\"coordinates\":null,\"formatter\":{\"id\":\"1476\"},\"group\":null,\"major_label_policy\":{\"id\":\"1477\"},\"ticker\":{\"id\":\"1413\"}},\"id\":\"1412\",\"type\":\"DatetimeAxis\"},{\"attributes\":{},\"id\":\"1424\",\"type\":\"ResetTool\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1439\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1410\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1420\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1054\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1404\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1473\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1053\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1437\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1439\"},\"nonselection_glyph\":{\"id\":\"1438\"},\"view\":{\"id\":\"1441\"}},\"id\":\"1440\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1408\",\"type\":\"LinearScale\"},{\"attributes\":{\"fill_color\":{\"value\":\"blue\"},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1443\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1474\",\"type\":\"AllLabels\"},{\"attributes\":{},\"id\":\"1477\",\"type\":\"AllLabels\"},{\"attributes\":{\"axis\":{\"id\":\"1412\"},\"coordinates\":null,\"group\":null,\"ticker\":null},\"id\":\"1415\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1423\",\"type\":\"SaveTool\"}],\"root_ids\":[\"1401\"]},\"title\":\"Bokeh Application\",\"version\":\"2.4.3\"}};\n const render_items = [{\"docid\":\"77c55f8b-56eb-4d43-b824-70ae6cecde50\",\"root_ids\":[\"1401\"],\"roots\":{\"1401\":\"2899bfad-82e0-488a-8429-7b02da60aab4\"}}];\n root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n }\n if (root.Bokeh !== undefined) {\n embed_document(root);\n } else {\n let attempts = 0;\n const timer = setInterval(function(root) {\n if (root.Bokeh !== undefined) {\n clearInterval(timer);\n embed_document(root);\n } else {\n attempts++;\n if (attempts > 100) {\n clearInterval(timer);\n console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n }\n }\n }, 10, root)\n }\n})(window);", + "application/javascript": "(function(root) {\n function embed_document(root) {\n const docs_json = {\"bb3868ef-0752-402f-883a-2db367d65446\":{\"defs\":[],\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1412\"}],\"center\":[{\"id\":\"1415\"},{\"id\":\"1419\"}],\"height\":450,\"left\":[{\"id\":\"1416\"}],\"renderers\":[{\"id\":\"1440\"},{\"id\":\"1446\"}],\"title\":{\"id\":\"1402\"},\"toolbar\":{\"id\":\"1428\"},\"x_range\":{\"id\":\"1404\"},\"x_scale\":{\"id\":\"1408\"},\"y_range\":{\"id\":\"1448\"},\"y_scale\":{\"id\":\"1410\"}},\"id\":\"1401\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"axis_label\":\"Commit date\",\"coordinates\":null,\"formatter\":{\"id\":\"1476\"},\"group\":null,\"major_label_policy\":{\"id\":\"1477\"},\"ticker\":{\"id\":\"1413\"}},\"id\":\"1412\",\"type\":\"DatetimeAxis\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"text\":\"Code Coverage\"},\"id\":\"1402\",\"type\":\"Title\"},{\"attributes\":{\"base\":24,\"mantissas\":[1,2,4,6,8,12],\"max_interval\":43200000.0,\"min_interval\":3600000.0,\"num_minor_ticks\":0},\"id\":\"1483\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1412\"},\"coordinates\":null,\"group\":null,\"ticker\":null},\"id\":\"1415\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1410\",\"type\":\"LinearScale\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1443\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1445\"},\"nonselection_glyph\":{\"id\":\"1444\"},\"view\":{\"id\":\"1447\"}},\"id\":\"1446\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"months\":[0,1,2,3,4,5,6,7,8,9,10,11]},\"id\":\"1488\",\"type\":\"MonthsTicker\"},{\"attributes\":{},\"id\":\"1420\",\"type\":\"PanTool\"},{\"attributes\":{\"days\":[1,15]},\"id\":\"1487\",\"type\":\"DaysTicker\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1003\"},\"glyph\":{\"id\":\"1437\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1439\"},\"nonselection_glyph\":{\"id\":\"1438\"},\"view\":{\"id\":\"1441\"}},\"id\":\"1440\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"days\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]},\"id\":\"1484\",\"type\":\"DaysTicker\"},{\"attributes\":{\"days\":[1,4,7,10,13,16,19,22,25,28]},\"id\":\"1485\",\"type\":\"DaysTicker\"},{\"attributes\":{\"days\":[1,8,15,22]},\"id\":\"1486\",\"type\":\"DaysTicker\"},{\"attributes\":{},\"id\":\"1421\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"months\":[0,6]},\"id\":\"1491\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"months\":[0,2,4,6,8,10]},\"id\":\"1489\",\"type\":\"MonthsTicker\"},{\"attributes\":{\"months\":[0,4,8]},\"id\":\"1490\",\"type\":\"MonthsTicker\"},{\"attributes\":{},\"id\":\"1473\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1425\",\"type\":\"HelpTool\"},{\"attributes\":{\"fill_color\":{\"value\":\"blue\"},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1443\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1474\",\"type\":\"AllLabels\"},{\"attributes\":{\"axis\":{\"id\":\"1416\"},\"coordinates\":null,\"dimension\":1,\"group\":null,\"ticker\":null},\"id\":\"1419\",\"type\":\"Grid\"},{\"attributes\":{\"axis_label\":\"Test coverage as a percentage of Python code\",\"coordinates\":null,\"formatter\":{\"id\":\"1473\"},\"group\":null,\"major_label_policy\":{\"id\":\"1474\"},\"ticker\":{\"id\":\"1417\"}},\"id\":\"1416\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1417\",\"type\":\"BasicTicker\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.2},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1445\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1404\",\"type\":\"DataRange1d\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"blue\"},\"hatch_alpha\":{\"value\":0.1},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"blue\"},\"size\":{\"value\":6},\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1444\",\"type\":\"Circle\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1447\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1477\",\"type\":\"AllLabels\"},{\"attributes\":{},\"id\":\"1423\",\"type\":\"SaveTool\"},{\"attributes\":{\"tools\":[{\"id\":\"1420\"},{\"id\":\"1421\"},{\"id\":\"1422\"},{\"id\":\"1423\"},{\"id\":\"1424\"},{\"id\":\"1425\"},{\"id\":\"1427\"}]},\"id\":\"1428\",\"type\":\"Toolbar\"},{\"attributes\":{\"overlay\":{\"id\":\"1426\"}},\"id\":\"1422\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"callback\":null,\"tooltips\":[[\"git ref\",\"@tooltip_label\"]]},\"id\":\"1427\",\"type\":\"HoverTool\"},{\"attributes\":{},\"id\":\"1424\",\"type\":\"ResetTool\"},{\"attributes\":{\"mantissas\":[1,2,5],\"max_interval\":500.0,\"num_minor_ticks\":0},\"id\":\"1481\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1437\",\"type\":\"Line\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1439\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1448\",\"type\":\"Range1d\"},{\"attributes\":{},\"id\":\"1408\",\"type\":\"LinearScale\"},{\"attributes\":{\"bottom_units\":\"screen\",\"coordinates\":null,\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"group\":null,\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"syncable\":false,\"top_units\":\"screen\"},\"id\":\"1426\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"base\":60,\"mantissas\":[1,2,5,10,15,20,30],\"max_interval\":1800000.0,\"min_interval\":1000.0,\"num_minor_ticks\":0},\"id\":\"1482\",\"type\":\"AdaptiveTicker\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"blue\",\"x\":{\"field\":\"date\"},\"y\":{\"field\":\"code_coverage\"}},\"id\":\"1438\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1476\",\"type\":\"DatetimeTickFormatter\"},{\"attributes\":{\"source\":{\"id\":\"1003\"}},\"id\":\"1441\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1492\",\"type\":\"YearsTicker\"},{\"attributes\":{\"data\":{\"cc\":{\"__ndarray__\":\"AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H8AAAAAAAD4fwAAAAAAAPh/AAAAAAAA+H+5jQbwFkj5P+f7qfHSTfk/6Ugu/yH9+D8c6+I2GsD5P5f/kH77Ovk/pN++Dpwz+T9t5/up8dL5P3lYqDXNO/k/u7iNBvAW+T+I9NvXgXP5Pz81XrpJDPo/rK3YX3ZP+T/XNO84RUf5P0aU9gZfmPk/AU2EDU+v+T+8BRIUP8b5P76fGi/dJPk/RiV1ApoI+j+Sy39Iv335PwXFjzF3Lfo/MuauJeSD+T/D0ytlGeL5P9uK/WX35Pk/rfpcbcX++T+OdXEbDeD5P/+ye/KwUPk/O3DOiNLe+T9DrWnecYr5PwIrhxbZzvk/CyQofoy5+T9uowG8BRL6P7AD54wo7fk/tTf4wmSq+T81XrpJDAL6P0tZhjjWxfk/U5YhjnVx+T+PwvUoXI/5PxZqTfOOU/k/guLHmLuW+T/EQq1p3nH5Pw4tsp3vp/k/ylTBqKRO+T8VHcnlP6T5P6pgVFInoPk/L90kBoGV+T9JLv8h/fb4PznWxW00gPk/bjSAt0CC+T+ppE5AE2H5P9obfGEyVfk/OdbFbTSA+T+XkA96Nqv0Pw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"code_coverage\":{\"__ndarray__\":\"PnlYqDXN2z/WVuwvuyfTPxB6Nqs+V9M/vJaQD3o21z8EVg4tsp3XP+PHmLuWkNc/lIeFWtO81z+Uh4Va07zXP9DVVuwvu9c/cvkP6bev1z/9h/Tb14HXP3sUrkfhetg/t2J/2T152D+QMXctIR/EP5Axdy0hH8Q/K/aX3ZOHxT+jkjoBTYTFP0T67evAOcM/ylTBqKROyD/8qfHSTWLIP662Yn/ZPck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACutmJ/2T3JPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"commit_hash\":[\"df25a9cfacd3d652361d2bd37f568af00acb2631\",\"b797390a43298a815f3ff57955cfdc71ecf3e866\",\"01a02d5f91b2f4a863eebe88a618974b0749d1c4\",\"dd847210082035d43b0273ae63a76a53cb8d2e12\",\"33779269e98cc882a5f066c462d8ec1eadf37a1a\",\"12890e029a7155b074b9b325d320d1798338e287\",\"66dafc08bd620d96deda7d526b0e4bfc3b086650\",\"a325819b3b03b84bd76ad455e3f9b4600744ba14\",\"8a2c1a610295c007f0222ce737723c341189811d\",\"c6bc79b0cfbc8ce5d6da0d33b68028157d2e93c0\",\"03e1f461c152e4f221fe92c834f2787680cf5772\",\"9e96d6c412b64fe76a57e7de8af3b00c21d18348\",\"2a98428f9c6fb9bb4302ae09809441bf3e7162b0\",\"9b4e85cf1b41ba7001aaba1a830b93e176f3dd43\",\"d18f4d263ecabf502242592f9d60815a07c7b89c\",\"a23241bb9e45078e36a4662d48c9d3fe0c3316e4\",\"c2006b0011a5df036c306c15e75763ec492dafda\",\"0c2adf3e702b6427da946a6ba9dbedbea22738be\",\"39c466000b1874e06a6f58da9c30bb877fc8d4d2\",\"8436fd78b002e5792f5d0dd1409332d171036d49\",\"07a45b66c5facfea06c40bd82e34040c97560640\",\"1d84538c334a502c6ad7df48b8cc2309d6a6436d\",\"4d528a3d6456621a382d409b5145a877b5414b88\",\"8c637b36b66069b216cb94ae87d4c0a91e9b211e\",\"4d23fa6dd78d0497deb4fd62783f0b3ee4204579\",\"015f6874c320efee2c0d1ae76eea4a5b043d69d6\",\"26f06d449da208ce64724b1463b07ad20746cbdc\",\"6b9d6bb8bec6e3ea548f5858e2a8ea5986264fc8\",\"b796bd0fd92ba6b91d590f6cb60bb7ab3bca9932\",\"780aef7c7b4b9cafea3e323d536a34a4af5818b4\",\"9f93ad9bf85e4a0e6baf5b62ea4b3ef143729861\",\"16628a0ba45a675df762245694e0a7666a3478f8\",\"01684c8559604344bd09791268131819a09770a8\",\"e9231fb893c765b723fa4c1e087a58761b6aa471\",\"219889e243ffc69c71b6f7747f5af751d5694de1\",\"6124d2a82a7a823722210bc2e8516d355ba19eb3\",\"f6e4287f712cc866893e71b1ea7a7546e4567bf9\",\"f2797fef396f2f19b02abb1f9555b678dac614f1\",\"b4e538f530048fec58eaca5170be82c67dbdcceb\",\"68820b715ed6b2c981aa11d29c0102e879280d79\",\"03deffeda91fa8d8ab188d57b9fa302a7be008e0\",\"0d2bfecc271d561f67050659684b4797af8ee740\",\"1d03a465593f56c99a64a576d185d4ed17b659f2\",\"78a953b7ef9a36b62e5b446c80ed68abfddbfb74\",\"6c4f70ffbf3d4d2922d41d0032ae1b93d8a23c99\",\"ab03282623d0262b20b8c132efcdcace2dace766\",\"d2f7a45af27a6b40027d6f6a0f4f0be0c6dee5d9\",\"98b23f3d517481b127f190f5f8b7ebfae7f8b6b2\",\"452425de723cc1640d999022389672caf9bffbd0\",\"85dadb1a566c9fa8dc84cb9837b98bd5d23b8d58\",\"432ee7f96c1f6cccd05a0034c86c720cdb63a3e6\",\"ebd70ecaef14c0e239337eb6e36506303378a31a\",\"77fa7155d55bdf3fd43e29f58fe57feffcb107cf\",\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\",\"d5d4b1346bd6acba9ba41b4bf546640de162a9d6\",\"7c879f1ce18b52d9b0a8eecf877d03e66afc975b\",\"2aa9f2a55686f2ee5dc407e8e0223eb25176d906\",\"5e5bb7f4e653621e7a81ff4bcaa27dbc1f759de7\",\"d91953a499dfb88b457a1e7a07903debbda4058b\",\"76742879c81c9baced49b9fc60abbf1d2eba65ff\",\"9c73a41eaca95bb718ac79980a1799dfa1c48cf3\",\"67104dd714de939be136646af68edd9643ddfcd3\"],\"commit_hash_8char\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"03e1f461\",\"9e96d6c4\",\"2a98428f\",\"9b4e85cf\",\"d18f4d26\",\"a23241bb\",\"c2006b00\",\"0c2adf3e\",\"39c46600\",\"8436fd78\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"16628a0b\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\",\"78a953b7\",\"6c4f70ff\",\"ab032826\",\"d2f7a45a\",\"98b23f3d\",\"452425de\",\"85dadb1a\",\"432ee7f9\",\"ebd70eca\",\"77fa7155\",\"d5d4b134\",\"d5d4b134\",\"7c879f1c\",\"2aa9f2a5\",\"5e5bb7f4\",\"d91953a4\",\"76742879\",\"9c73a41e\",\"67104dd7\"],\"date\":{\"__ndarray__\":\"AACAyDfgd0IAAEDF0+F3QgAAACsm4ndCAACA9srid0IAAICNFOR3QgAAQPNm5HdCAAAAWbnkd0IAAMC+C+V3QgAAgCRe5XdCAACAJF7ld0IAAIC7p+Z3QgAAQCH65ndCAACA3Krvd0IAAABtLPR3QgAAQJ4j9XdCAAAAvML/d0IAAMBWixh4QgAAQIU5NHhCAAAAWyVJeEIAAAAn5WJ4QgAAACflYnhCAACAt2ZneEIAAEAduWd4QgAAQLQCaXhCAABAtAJpeEIAAMB/p2l4QgAAgHxDa3hCAACAfENreEIAAIB8Q2t4QgAAQOKVa3hCAABA4pVreEIAAEDilWt4QgAAwNvNbnhCAAAADcVveEIAAEA+vHB4QgAAAGnrdHhCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAABcW3t4QgAAAFxbe3hCAAAAXFt7eEIAAMDvQH54QgAAwO9AfnhCAABAu+V+eEIAAEC75X54QgAAQLvlfnhCAABAu+V+eEIAAAAhOH94QgAAQFIvgHhCAABAUi+AeEIAAAC4gYB4QgAAwB3UgHhCAADAtB2CeEIAAMC0HYJ4QgAAwLQdgnhCAADAtB2CeEIAAMAQRId4QgAAwMiQkXhCAAAA+oeSeEIAAAD6h5J4Qg==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"gauss\":{\"__ndarray__\":\"GJXUCWgi9D+Hp1fKMsTzP0Jg5dAi2+k/xLEubqMB6j/LEMe6uI3qP9CzWfW52uo/1zTvOEVH6j/+1HjpJjHqPzhnRGlv8Ok/FR3J5T+k6T/MXUvIBz3pP99PjZduEuk/kst/SL996T/129eBc0bsPwmKH2PuWuw/P8bctYR87D+IY13cRgPsPw1xrIvbaOw/0m9fB84Z7T+94xQdyeXrP7snDwu1pus/r5RliGNd7D9sCfmgZ7PsP8UgsHJoke0/lWWIY13c7D8dWmQ730/tP2iz6nO1Fe0/46WbxCCw7D8AkX77OnDsP4Za07zjFO0/3+ALk6mC7T9txf6ye/LsP8X+snvysOw/XrpJDAIr7T+yne+nxkvtP+M2GsBbIO0/9+RhodY07T+h1jTvOEXtPzhnRGlv8O0/QKTfvg6c7T+qYFRSJ6DtP/mgZ7Pqc+0/6bevA+eM7D9R2ht8YTLtPwpoImx4eu0/1sVtNIC37D9oImx4eqXsP5MYBFYOLew/LSEf9GxW7T9n1edqK/brPz2bVZ+rrew/RUdy+Q/p6z9hVFInoInsP+m3rwPnjOw/KqkT0ETY7D9ZF7fRAN7sP9qs+lxtxew/N4lBYOXQ7D/D9Shcj8LrP6abxCCwcuw/kDF3LSEf7D+si9toAG/rPw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"gch\":{\"__ndarray__\":\"X5hMFYxK+j9IUPwYc9f8P1UwKqkT0PU/BoGVQ4ts9T+gibDh6ZX2P+M2GsBbIPY/FYxK6gQ09j8OvjCZKhj2P9cS8kHPZvY/5WGh1jTv9j/LEMe6uI32P2q8dJMYBPY/f/s6cM6I9T8MAiuHFtn2P807TtGRXPc/ofgx5q4l9z+NKO0NvjD3P8rDQq1p3vY/bHh6pSxD9z/dJAaBlUP3P8dLN4lBYPc/63O1FfvL9j9UdCSX/5D2PzvfT42Xbvc/tFn1udqK9z/sL7snDwv4PyNKe4MvTPc/1QloImx49z+Hp1fKMsT2P4QNT6+UZfc/bjSAt0CC9z90JJf/kH73PxBYObTIdvc/CKwcWmQ79z+Cc0aU9gb3P1D8GHPXEvc/7FG4HoXr9z/l8h/Sb1/3P9jw9EpZhvc/MuauJeSD9z8JG55eKcv3P4BIv30dOPc/oyO5/If09j8xCKwcWmT3P2dEaW/whfc/JzEIrBxa9j/0bFZ9rrb2P73jFB3J5fY/ArwFEhQ/9z8yVTAqqRP3P7Pqc7UV+/Y/d76fGi/d9j/l0CLb+X72P2lv8IXJVPc/GeJYF7fR9j+Nl24Sg8D2P5kqGJXUCfc/bAn5oGez9j8J+aBns+r2P9V46SYxCPc/UiegibDh9j8nwoanV8rwPw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61],\"jensen\":{\"__ndarray__\":\"WKg1zTtO9D/vOEVHcvnlP2EyVTAqqdc/fdCzWfW51j8f9GxWfa7WP9UJaCJseNY/r5RliGNd2D8awFsgQfHXP0Ck374OnNc/5fIf0m9f1z988rBQa5rXP6+UZYhjXdg/eVioNc07zj/Q1VbsL7vPP4y5awn5oM8/XI/C9Shczz9d3EYDeAvQP2WqYFRSJ9A/GJXUCWgi0D+coiO5/IfQP4qO5PIf0s8/f2q8dJMY0D+vlGWIY13QPzqSy39Iv9E/5q4l5IOe0T/zH9JvXwfSP+viNhrAW9A/QmDl0CLb0T8yVTAqqRPQP1InoImw4dE/ZRniWBe30T+2hHzQs1nRP5huEoPAytE/3bWEfNCz0T99rrZif9nRP451cRsN4NE/WvW52or90T8E54wo7Q3SP3uDL0ymCtI/CD2bVZ+r0T8cfGEyVTDSPw8LtaZ5x9E/pgpGJXUC0j+TGARWDi3SPzY8vVKWIdI/HThnRGlv0D8/xty1hHzQP8/3U+Olm9A/tTf4wmSq0D8BTYQNT6/QP8/3U+Olm9A/Ns07TtGR0D9oImx4eqXQP2/whclUwdA/aCJseHql0D/r4jYawFvQP9Ei2/l+atA/fT81XrpJ0D8u/yH99nXQP3RGlPYGX9A/xf6ye/Kw0D8Zc9cS8kHTPw==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[62]},\"tooltip_label\":[\"df25a9cf\",\"b797390a\",\"01a02d5f\",\"dd847210\",\"33779269\",\"12890e02\",\"66dafc08\",\"a325819b\",\"8a2c1a61\",\"c6bc79b0\",\"PR #56\",\"v3.0rc1\",\"PR #317\",\"v3.0\",\"v3.0.1\",\"v3.1\",\"v3.1.1\",\"v3.2\",\"v3.2.1\",\"v3.2.2\",\"07a45b66\",\"1d84538c\",\"4d528a3d\",\"8c637b36\",\"4d23fa6d\",\"015f6874\",\"26f06d44\",\"6b9d6bb8\",\"b796bd0f\",\"780aef7c\",\"9f93ad9b\",\"v3.3\",\"01684c85\",\"e9231fb8\",\"219889e2\",\"6124d2a8\",\"f6e4287f\",\"f2797fef\",\"b4e538f5\",\"68820b71\",\"03deffed\",\"0d2bfecc\",\"1d03a465\",\"78a953b7\",\"6c4f70ff\",\"ab032826\",\"d2f7a45a\",\"98b23f3d\",\"452425de\",\"85dadb1a\",\"432ee7f9\",\"ebd70eca\",\"77fa7155\",\"d5d4b134\",\"d5d4b134\",\"7c879f1c\",\"2aa9f2a5\",\"v3.4\",\"d91953a4\",\"76742879\",\"9c73a41e\",\"67104dd7\"]},\"selected\":{\"id\":\"1054\"},\"selection_policy\":{\"id\":\"1053\"}},\"id\":\"1003\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1054\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1053\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"num_minor_ticks\":5,\"tickers\":[{\"id\":\"1481\"},{\"id\":\"1482\"},{\"id\":\"1483\"},{\"id\":\"1484\"},{\"id\":\"1485\"},{\"id\":\"1486\"},{\"id\":\"1487\"},{\"id\":\"1488\"},{\"id\":\"1489\"},{\"id\":\"1490\"},{\"id\":\"1491\"},{\"id\":\"1492\"}]},\"id\":\"1413\",\"type\":\"DatetimeTicker\"}],\"root_ids\":[\"1401\"]},\"title\":\"Bokeh Application\",\"version\":\"2.4.3\"}};\n const render_items = [{\"docid\":\"bb3868ef-0752-402f-883a-2db367d65446\",\"root_ids\":[\"1401\"],\"roots\":{\"1401\":\"d87aac59-c6f2-4222-a00a-6786cf9e2391\"}}];\n root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n }\n if (root.Bokeh !== undefined) {\n embed_document(root);\n } else {\n let attempts = 0;\n const timer = setInterval(function(root) {\n if (root.Bokeh !== undefined) {\n clearInterval(timer);\n embed_document(root);\n } else {\n attempts++;\n if (attempts > 100) {\n clearInterval(timer);\n console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n }\n }\n }, 10, root)\n }\n})(window);", "application/vnd.bokehjs_exec.v0+json": "" }, "metadata": { diff --git a/docs/dev_guide.md b/docs/dev_guide.md index 290325531..ac1b32da2 100644 --- a/docs/dev_guide.md +++ b/docs/dev_guide.md @@ -216,7 +216,7 @@ compiling, a file should be located at ``docs/_build/html/index.html``. This file can be opened in any browser. ```bash -pip install -e .["docs"] +pip install -e ".[docs]" jupyter-book build docs/ # Lots of output to the terminal here... diff --git a/docs/empirical_gauss_model.md b/docs/empirical_gauss_model.md index 12078dcf2..c1c9fddf5 100644 --- a/docs/empirical_gauss_model.md +++ b/docs/empirical_gauss_model.md @@ -10,7 +10,9 @@ have been reorganized to provide simpler tuning and data fitting. The velocity deficit at a point $(x, y, z)$ in the wake follows a Gaussian curve, i.e., + $$ \frac{u}{U_\infty} = 1 - Ce^{-\frac{(y-\delta_y)^2}{2\sigma_y^2} -\frac{(z-z_h-\delta_z)^2}{2\sigma_z^2}} $$ + where the $(x, y, z)$ origin is at the turbine location (at ground level). The terms $C$, $\sigma_y$, $\sigma_z$, $\delta_y$, and $\delta_z$ all depend on the downstream location $x$. diff --git a/docs/examples.md b/docs/examples.md index cc38c4c2e..e87627cd4 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -84,8 +84,19 @@ Define non-uniform (heterogeneous) atmospheric conditions by specifying speedups at locations throughout the farm. Show plots of the impact on wind turbine wakes. -### 16b_heterogenaity_multiple_ws_wd.py -Illustrate usage of heterogenaity with multiple wind speeds and directions. +### 16b_heterogeneity_multiple_ws_wd.py +Illustrate usage of heterogeneity with multiple wind speeds and directions. + +## 16c_optimize_layout_with_heterogeneity.py +This example shows a layout optimization using the geometric yaw option. It +combines elements of examples 15 (layout optimization) and 16 (heterogeneous +inflow) for demonstrative purposes. If you haven't yet run those examples, +we recommend you try them first. + +Heterogeneity in the inflow provides the necessary driver for coupled yaw +and layout optimization to be worthwhile. First, a layout optimization is +run without coupled yaw optimization; then a coupled optimization is run to +show the benefits of coupled optimization when flows are heterogeneous. ### 17_multiple_turbine_types.py Load an input file that describes a wind farm with two turbines @@ -99,6 +110,10 @@ of a turbine layout within FLORIS. Demonstrates the definition of a floating turbine and how to enable the effects of tilt on Cp and Ct. +For further examples on floating wind turbines, see also examples +25 (vertical wake deflection by a forced tilt angle) and 29 (comparison between +a fixed-bottom and floating wind farm). + ### 25_tilt_driven_vertical_wake_deflection.py This example demonstrates vertical wake deflections due to the tilt angle when running @@ -107,6 +122,10 @@ vertical deflections at this time. Also be aware that this example uses a potent unrealistic tilt angle, 15 degrees, to highlight the wake deflection. Moreover, the magnitude of vertical deflections due to tilt has not been validated. +For further examples on floating wind turbines, see also examples +24 (effects of tilt on turbine power and thrust coefficients) and 29 +(comparison between a fixed-bottom and floating wind farm). + ### 26_empirical_gauss_velocity_deficit_parameters.py This example illustrates the main parameters of the Empirical Gaussian @@ -127,6 +146,32 @@ mast across all wind directions (at a fixed free stream wind speed). Try different values for met_mast_option to vary the location of the met mast within the two-turbine farm. +### 29_floating_vs_fixedbottom_farm.py + +Compares a fixed-bottom wind farm (with a gridded layout) to a floating +wind farm with the same layout. Includes: +- Turbine-by-turbine power comparison for a single wind speed and direction +- Flow visualizations for a single wind speed and direction +- AEP calculations based on an example wind rose. + +For further examples on floating wind turbines, see also examples +24 (effects of tilt on turbine power and thrust coefficients) and 25 +(vertical wake deflection by a forced tilt angle). + +### 30_multi_dimensional_cp_ct.py + +This example showcases the capability of using multi-dimensional Cp/Ct data in turbine defintions +dependent on external conditions. Specifically, fictional data for varying Cp/Ct values based on +wave period, Ts, and wave height, Hs, is used, showing the user how to setup the turbine +definition and input file. Also demonstrated is the different method for getting turbine +powers when using multi-dimensional Cp/Ct data. + +### 31_multi_dimensional_cp_ct_2Hs.py + +This example builds on example 30. Specifically, fictional data for varying Cp/Ct values based on +wave period, Ts, and wave height, Hs, is used to show the difference in power performance for +different wave heights. + ## Optimization These examples demonstrate use of the optimization routines diff --git a/examples/01_opening_floris_computing_power.py b/examples/01_opening_floris_computing_power.py index 049718f38..b006dfe4d 100644 --- a/examples/01_opening_floris_computing_power.py +++ b/examples/01_opening_floris_computing_power.py @@ -51,7 +51,7 @@ print(turbine_powers) print("Shape: ",turbine_powers.shape) -# Single wind speed and wind direction +# Single wind speed and multiple wind directions print('\n========================= Single Wind Direction and Multiple Wind Speeds ===============') @@ -64,7 +64,7 @@ print(turbine_powers) print("Shape: ",turbine_powers.shape) -# Single wind speed and wind direction +# Multiple wind speeds and multiple wind directions print('\n========================= Multiple Wind Directions and Multiple Wind Speeds ============') wind_directions = np.array([260., 270., 280.]) diff --git a/examples/02_visualizations.py b/examples/02_visualizations.py index 669e91fa0..4b65f8e9d 100644 --- a/examples/02_visualizations.py +++ b/examples/02_visualizations.py @@ -18,10 +18,6 @@ import floris.tools.visualization as wakeviz from floris.tools import FlorisInterface -from floris.tools.visualization import ( - calculate_horizontal_plane_with_turbines, - visualize_cut_plane, -) """ @@ -78,13 +74,28 @@ # Create the plots fig, ax_list = plt.subplots(3, 1, figsize=(10, 8)) ax_list = ax_list.flatten() -wakeviz.visualize_cut_plane(horizontal_plane, ax=ax_list[0], title="Horizontal") -wakeviz.visualize_cut_plane(y_plane, ax=ax_list[1], title="Streamwise profile") -wakeviz.visualize_cut_plane(cross_plane, ax=ax_list[2], title="Spanwise profile") +wakeviz.visualize_cut_plane( + horizontal_plane, + ax=ax_list[0], + label_contours=True, + title="Horizontal" +) +wakeviz.visualize_cut_plane( + y_plane, + ax=ax_list[1], + label_contours=True, + title="Streamwise profile" +) +wakeviz.visualize_cut_plane( + cross_plane, + ax=ax_list[2], + label_contours=True, + title="Spanwise profile" +) # Some wake models may not yet have a visualization method included, for these cases can use # a slower version which scans a turbine model to produce the horizontal flow -horizontal_plane_scan_turbine = calculate_horizontal_plane_with_turbines( +horizontal_plane_scan_turbine = wakeviz.calculate_horizontal_plane_with_turbines( fi, x_resolution=20, y_resolution=10, @@ -92,9 +103,10 @@ ) fig, ax = plt.subplots() -visualize_cut_plane( +wakeviz.visualize_cut_plane( horizontal_plane_scan_turbine, ax=ax, + label_contours=True, title="Horizontal (coarse turbine scan method)", ) diff --git a/examples/12_optimize_yaw_in_parallel.py b/examples/12_optimize_yaw_in_parallel.py index 5575779d1..33c996dc1 100644 --- a/examples/12_optimize_yaw_in_parallel.py +++ b/examples/12_optimize_yaw_in_parallel.py @@ -67,12 +67,13 @@ def load_windrose(): ) # Pour this into a parallel computing interface + parallel_interface = "concurrent" fi_aep_parallel = ParallelComputingInterface( fi=fi_aep, max_workers=max_workers, n_wind_direction_splits=max_workers, n_wind_speed_splits=1, - use_mpi4py=False, + interface=parallel_interface, print_timings=True, ) @@ -113,7 +114,7 @@ def load_windrose(): max_workers=max_workers, n_wind_direction_splits=max_workers, n_wind_speed_splits=1, - use_mpi4py=False, + interface=parallel_interface, print_timings=True, ) diff --git a/examples/14_compare_yaw_optimizers.py b/examples/14_compare_yaw_optimizers.py index 9e22f6872..1c4e29c31 100644 --- a/examples/14_compare_yaw_optimizers.py +++ b/examples/14_compare_yaw_optimizers.py @@ -18,6 +18,9 @@ import numpy as np from floris.tools import FlorisInterface +from floris.tools.optimization.yaw_optimization.yaw_optimizer_geometric import ( + YawOptimizationGeometric, +) from floris.tools.optimization.yaw_optimization.yaw_optimizer_scipy import YawOptimizationScipy from floris.tools.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR @@ -31,6 +34,13 @@ We then perform the optimization using both methods. Finally, we compare the time it took to find the optimal angles and plot the optimal yaw angles and resulting wind farm powers. + +The example now also compares the Geometric Yaw optimizer, which is fast +a method to find approximately optimal yaw angles based on the wind farm geometry. Its +main use case is for coupled layout and yaw optimization. +see floris.tools.optimization.yaw_optimization.yaw_optimizer_geometric.py and the paper online +at https://wes.copernicus.org/preprints/wes-2023-1/. See also example 16c. + """ # Load the default example floris object @@ -58,17 +68,29 @@ df_opt_sr = yaw_opt_sr.optimize() time_sr = timerpc() - start_time +print("Performing optimizations with Geometric Yaw...") +start_time = timerpc() +yaw_opt_geo = YawOptimizationGeometric(fi) +df_opt_geo = yaw_opt_geo.optimize() +time_geo = timerpc() - start_time + + + # Print time spent -print("\n Time spent, Serial Refine: {:.2f} s.".format(time_sr)) +print("\n Time spent, Geometric Yaw: {:.2f} s.".format(time_geo)) +print(" Time spent, Serial Refine: {:.2f} s.".format(time_sr)) print(" Time spent, SciPy (SLSQP): {:.2f} s.\n".format(time_scipy)) # Split out the turbine results +yaw_angles_opt_geo = np.vstack(df_opt_geo.yaw_angles_opt) yaw_angles_opt_sr = np.vstack(df_opt_sr.yaw_angles_opt) yaw_angles_opt_scipy = np.vstack(df_opt_scipy.yaw_angles_opt) + # Yaw results for t in range(3): fig, ax = plt.subplots() + ax.plot(df_opt_geo.wind_direction, yaw_angles_opt_geo[:, t],color='m',label='Geometric') ax.plot(df_opt_sr.wind_direction, yaw_angles_opt_sr[:, t],color='r',label='Serial Refine') ax.plot(df_opt_scipy.wind_direction, yaw_angles_opt_scipy[:, t],'--', color='g', label='SciPy') ax.grid(True) @@ -77,7 +99,15 @@ ax.grid(True) ax.set_title("Turbine {:d}".format(t)) -# Power results +# Power results ============== + +# Before plotting results, need to compute values for GEOOPT since it doesn't compute +# power within the optimization +yaw_angles_opt_geo_3d = np.expand_dims(yaw_angles_opt_geo, axis=1) +fi.calculate_wake(yaw_angles=yaw_angles_opt_geo_3d) +geo_farm_power = fi.get_farm_power().squeeze() + + fig, ax = plt.subplots() ax.plot( df_opt_sr.wind_direction, @@ -85,6 +115,12 @@ color='k', label='Baseline' ) +ax.plot( + df_opt_geo.wind_direction, + geo_farm_power, + color='m', + label='Optimized, Gemoetric' +) ax.plot( df_opt_sr.wind_direction, df_opt_sr.farm_power_opt, @@ -103,4 +139,75 @@ ax.legend() ax.grid(True) +# Finally, compare the overall the power gains + +fig, ax = plt.subplots() + +ax.plot( + df_opt_geo.wind_direction, + geo_farm_power - df_opt_sr.farm_power_baseline, + color='m', + label='Optimized, Gemoetric' +) +ax.plot( + df_opt_sr.wind_direction, + df_opt_sr.farm_power_opt - df_opt_sr.farm_power_baseline, + color='r', + label='Optimized, Serial Refine' +) +ax.plot( + df_opt_scipy.wind_direction, + df_opt_scipy.farm_power_opt - df_opt_scipy.farm_power_baseline, + '--', + color='g', + label='Optimized, SciPy' +) +ax.set_ylabel('Increase in Wind Farm Power (W)') +ax.set_xlabel('Wind Direction (deg)') +ax.legend() +ax.grid(True) + + +# Finally, make a quick bar plot comparing nomimal power and nomimal uplift +total_power_uplift_geo = np.sum(geo_farm_power - df_opt_sr.farm_power_baseline) +total_power_uplift_sr = np.sum(df_opt_sr.farm_power_opt - df_opt_sr.farm_power_baseline) +total_power_uplift_scipy = np.sum(df_opt_scipy.farm_power_opt - df_opt_scipy.farm_power_baseline) + +# Plot on the left subplot a barplot comparing the uplift normalized to scipy and on the right +# subplot a barplot of total time normalzed to scipy +fig, axarr = plt.subplots(1,2,figsize=(10,5)) + +ax = axarr[0] +ax.bar( + [0, 1, 2], + [ + total_power_uplift_geo / total_power_uplift_scipy, + total_power_uplift_sr / total_power_uplift_scipy, + 1.0, + ], + color=['m', 'r', 'g'], +) +ax.set_xticks([0, 1, 2]) +ax.set_xticklabels(['Geometric', 'Serial Refine', 'SciPy']) +ax.set_ylabel('Normalized Power Gain') +ax.grid(True) + +ax = axarr[1] +ax.bar( + [0, 1, 2], + [ + time_geo / time_scipy, + time_sr / time_scipy, + 1.0, + ], + color=['m', 'r', 'g'], +) +ax.set_xticks([0, 1, 2]) +ax.set_xticklabels(['Geometric', 'Serial Refine', 'SciPy']) +ax.set_ylabel('Normalized Computation Time') +ax.grid(True) + +# Change to semi-logy +axarr[1].set_yscale('log') + plt.show() diff --git a/examples/15_optimize_layout.py b/examples/15_optimize_layout.py index 2e44fad31..68ff4a895 100644 --- a/examples/15_optimize_layout.py +++ b/examples/15_optimize_layout.py @@ -15,6 +15,7 @@ import os +import matplotlib.pyplot as plt import numpy as np from floris.tools import FlorisInterface @@ -85,3 +86,5 @@ f'from {base_aep:.1f} MWh to {opt_aep:.1f} MWh' ) layout_opt.plot_layout_opt_results() + +plt.show() diff --git a/examples/16_heterogeneous_inflow.py b/examples/16_heterogeneous_inflow.py index 3f04d5bc4..3dedf05e7 100644 --- a/examples/16_heterogeneous_inflow.py +++ b/examples/16_heterogeneous_inflow.py @@ -70,17 +70,30 @@ # Create the plots fig, ax_list = plt.subplots(3, 1, figsize=(10, 8)) ax_list = ax_list.flatten() -visualize_cut_plane(horizontal_plane_2d, ax=ax_list[0], title="Horizontal", color_bar=True) +visualize_cut_plane( + horizontal_plane_2d, + ax=ax_list[0], + title="Horizontal", + color_bar=True, + label_contours=True +) ax_list[0].set_xlabel('x') ax_list[0].set_ylabel('y') -visualize_cut_plane(y_plane_2d, ax=ax_list[1], title="Streamwise profile", color_bar=True) +visualize_cut_plane( + y_plane_2d, + ax=ax_list[1], + title="Streamwise profile", + color_bar=True, + label_contours=True +) ax_list[1].set_xlabel('x') ax_list[1].set_ylabel('z') visualize_cut_plane( cross_plane_2d, ax=ax_list[2], title="Spanwise profile at 500m downstream", - color_bar=True + color_bar=True, + label_contours=True ) ax_list[2].set_xlabel('y') ax_list[2].set_ylabel('z') @@ -136,7 +149,8 @@ horizontal_plane_3d, ax=ax_list[0], title="Horizontal", - color_bar=True + color_bar=True, + label_contours=True ) ax_list[0].set_xlabel('x') ax_list[0].set_ylabel('y') @@ -144,7 +158,8 @@ y_plane_3d, ax=ax_list[1], title="Streamwise profile", - color_bar=True + color_bar=True, + label_contours=True ) ax_list[1].set_xlabel('x') ax_list[1].set_ylabel('z') @@ -152,7 +167,8 @@ cross_plane_3d, ax=ax_list[2], title="Spanwise profile at 500m downstream", - color_bar=True + color_bar=True, + label_contours=True ) ax_list[2].set_xlabel('y') ax_list[2].set_ylabel('z') diff --git a/examples/16b_heterogenaity_multiple_ws_wd.py b/examples/16b_heterogeneity_multiple_ws_wd.py similarity index 100% rename from examples/16b_heterogenaity_multiple_ws_wd.py rename to examples/16b_heterogeneity_multiple_ws_wd.py diff --git a/examples/16c_optimize_layout_with_heterogeneity.py b/examples/16c_optimize_layout_with_heterogeneity.py new file mode 100644 index 000000000..ca27e3d7f --- /dev/null +++ b/examples/16c_optimize_layout_with_heterogeneity.py @@ -0,0 +1,171 @@ +# Copyright 2022 NREL + +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# See https://floris.readthedocs.io for documentation + + +import os + +import matplotlib.pyplot as plt +import numpy as np + +from floris.tools import FlorisInterface +from floris.tools.optimization.layout_optimization.layout_optimization_scipy import ( + LayoutOptimizationScipy, +) + + +""" +This example shows a layout optimization using the geometric yaw option. It +combines elements of examples 15 (layout optimization) and 16 (heterogeneous +inflow) for demonstrative purposes. If you haven't yet run those examples, +we recommend you try them first. + +Heterogeneity in the inflow provides the necessary driver for coupled yaw +and layout optimization to be worthwhile. First, a layout optimization is +run without coupled yaw optimization; then a coupled optimization is run to +show the benefits of coupled optimization when flows are heterogeneous. +""" + +# Initialize the FLORIS interface fi +file_dir = os.path.dirname(os.path.abspath(__file__)) +fi = FlorisInterface('inputs/gch.yaml') + +# Setup 2 wind directions (due east and due west) +# and 1 wind speed with uniform probability +wind_directions = [270., 90.] +n_wds = len(wind_directions) +wind_speeds = [8.0] +# Shape frequency distribution to match number of wind directions and wind speeds +freq = np.ones((len(wind_directions), len(wind_speeds))) +freq = freq / freq.sum() + +# The boundaries for the turbines, specified as vertices +D = 126.0 # rotor diameter for the NREL 5MW +size_D = 12 +boundaries = [ + (0.0, 0.0), + (size_D * D, 0.0), + (size_D * D, 0.1), + (0.0, 0.1), + (0.0, 0.0) +] + +# Set turbine locations to 4 turbines at corners of the rectangle +# (optimal without flow heterogeneity) +layout_x = [0.1, 0.3*size_D*D, 0.6*size_D*D] +layout_y = [0, 0, 0] + +# Generate exaggerated heterogeneous inflow (same for all wind directions) +speed_multipliers = np.repeat(np.array([0.5, 1.0, 0.5, 1.0])[None,:], n_wds, axis=0) +x_locs = [0, size_D * D, 0, size_D * D] +y_locs = [-D, -D, D, D] + +# Create the configuration dictionary to be used for the heterogeneous inflow. +heterogenous_inflow_config = { + 'speed_multipliers': speed_multipliers, + 'x': x_locs, + 'y': y_locs, +} + +fi.reinitialize( + layout_x=layout_x, + layout_y=layout_y, + wind_directions=wind_directions, + wind_speeds=wind_speeds, + heterogenous_inflow_config=heterogenous_inflow_config +) + +# Setup and solve the layout optimization problem without heterogeneity +maxiter = 100 +layout_opt = LayoutOptimizationScipy( + fi, + boundaries, + freq=freq, + min_dist=2*D, + optOptions={"maxiter":maxiter} +) + +# Run the optimization +np.random.seed(0) +sol = layout_opt.optimize() + +# Get the resulting improvement in AEP +print('... calcuating improvement in AEP') +fi.calculate_wake() +base_aep = fi.get_farm_AEP(freq=freq) / 1e6 +fi.reinitialize(layout_x=sol[0], layout_y=sol[1]) +fi.calculate_wake() +opt_aep = fi.get_farm_AEP(freq=freq) / 1e6 +percent_gain = 100 * (opt_aep - base_aep) / base_aep + +# Print and plot the results +print(f'Optimal layout: {sol}') +print( + f'Optimal layout improves AEP by {percent_gain:.1f}% ' + f'from {base_aep:.1f} MWh to {opt_aep:.1f} MWh' +) +layout_opt.plot_layout_opt_results() +ax = plt.gca() +fig = plt.gcf() +sm = ax.tricontourf(x_locs, y_locs, speed_multipliers[0], cmap="coolwarm") +fig.colorbar(sm, ax=ax, label="Speed multiplier") +ax.legend(["Initial layout", "Optimized layout", "Optimization boundary"]) +ax.set_title("Geometric yaw disabled") + + +# Rerun the layout optimization with geometric yaw enabled +print("\nReoptimizing with geometric yaw enabled.") +fi.reinitialize(layout_x=layout_x, layout_y=layout_y) +layout_opt = LayoutOptimizationScipy( + fi, + boundaries, + freq=freq, + min_dist=2*D, + enable_geometric_yaw=True, + optOptions={"maxiter":maxiter} +) + +# Run the optimization +np.random.seed(0) +sol = layout_opt.optimize() + +# Get the resulting improvement in AEP +print('... calcuating improvement in AEP') +fi.calculate_wake() +base_aep = fi.get_farm_AEP(freq=freq) / 1e6 +fi.reinitialize(layout_x=sol[0], layout_y=sol[1]) +fi.calculate_wake() +opt_aep = fi.get_farm_AEP(freq=freq, yaw_angles=layout_opt.yaw_angles) / 1e6 +percent_gain = 100 * (opt_aep - base_aep) / base_aep + +# Print and plot the results +print(f'Optimal layout: {sol}') +print( + f'Optimal layout improves AEP by {percent_gain:.1f}% ' + f'from {base_aep:.1f} MWh to {opt_aep:.1f} MWh' +) +layout_opt.plot_layout_opt_results() +ax = plt.gca() +fig = plt.gcf() +sm = ax.tricontourf(x_locs, y_locs, speed_multipliers[0], cmap="coolwarm") +fig.colorbar(sm, ax=ax, label="Speed multiplier") +ax.legend(["Initial layout", "Optimized layout", "Optimization boundary"]) +ax.set_title("Geometric yaw enabled") + +print( + 'Turbine geometric yaw angles for wind direction {0:.2f}'.format(wind_directions[1])\ + +' and wind speed {0:.2f} m/s:'.format(wind_speeds[0]), + f'{layout_opt.yaw_angles[1,0,:]}' +) + +plt.show() diff --git a/examples/18_check_turbine.py b/examples/18_check_turbine.py index ec9d9b20e..e71f321ff 100644 --- a/examples/18_check_turbine.py +++ b/examples/18_check_turbine.py @@ -42,6 +42,8 @@ turbines = os.listdir('../floris/turbine_library') turbines = [t for t in turbines if 'yaml' in t] turbines = [t.strip('.yaml') for t in turbines] +# Remove multi-dimensional Cp/Ct turbine definitions as they require different handling +turbines = [i for i in turbines if ('multi_dim' not in i)] # Declare a set of figures for comparing cp and ct across models fig_cp_ct, axarr_cp_ct = plt.subplots(2,1,sharex=True,figsize=(10,10)) diff --git a/examples/25_tilt_driven_vertical_wake_deflection.py b/examples/25_tilt_driven_vertical_wake_deflection.py index f7897fe53..1725e4134 100644 --- a/examples/25_tilt_driven_vertical_wake_deflection.py +++ b/examples/25_tilt_driven_vertical_wake_deflection.py @@ -41,7 +41,7 @@ # Figure settings x_bounds = [-500, 3000] y_bounds = [-250, 250] -z_bounds = [0, 500] +z_bounds = [0.001, 500] cross_plane_locations = [10, 1200, 2500] horizontal_plane_location=90.0 diff --git a/examples/26_empirical_gauss_velocity_deficit_parameters.py b/examples/26_empirical_gauss_velocity_deficit_parameters.py index a34d4cc61..b2787059c 100644 --- a/examples/26_empirical_gauss_velocity_deficit_parameters.py +++ b/examples/26_empirical_gauss_velocity_deficit_parameters.py @@ -42,7 +42,7 @@ def generate_wake_visualization(fi: FlorisInterface, title=None): # Using the FlorisInterface functions, get 2D slices. x_bounds = [-500, 3000] y_bounds = [-250, 250] - z_bounds = [0, 500] + z_bounds = [0.001, 500] cross_plane_locations = [10, 1200, 2500] horizontal_plane_location = 90.0 streamwise_plane_location = 0.0 @@ -150,7 +150,7 @@ def generate_wake_visualization(fi: FlorisInterface, title=None): # Increase the base recovery rate fi_dict_mod = copy.deepcopy(fi_dict) fi_dict_mod['wake']['wake_velocity_parameters']['empirical_gauss']\ - ['wake_expansion_rates'] = [0.02, 0.01] + ['wake_expansion_rates'] = [0.03, 0.015] fi = FlorisInterface(fi_dict_mod) fi.reinitialize( wind_speeds=[8.0], diff --git a/examples/27_empirical_gauss_deflection_parameters.py b/examples/27_empirical_gauss_deflection_parameters.py index 2ddb8a647..5e453a7ad 100644 --- a/examples/27_empirical_gauss_deflection_parameters.py +++ b/examples/27_empirical_gauss_deflection_parameters.py @@ -39,12 +39,14 @@ yaw_angles = np.array(first_three_yaw_angles + [0.]*(num_in_row-3))\ [None, None, :] +print("Turbine yaw angles (degrees): ", yaw_angles[0,0,:]) + # Define function for visualizing wakes def generate_wake_visualization(fi, title=None): # Using the FlorisInterface functions, get 2D slices. x_bounds = [-500, 3000] y_bounds = [-250, 250] - z_bounds = [0, 500] + z_bounds = [0.001, 500] cross_plane_locations = [10, 1200, 2500] horizontal_plane_location = 90.0 streamwise_plane_location = 0.0 @@ -153,8 +155,6 @@ def generate_wake_visualization(fi, title=None): # Increase the maximum deflection attained fi_dict_mod = copy.deepcopy(fi_dict) -print(fi_dict_mod['wake']['wake_deflection_parameters']['empirical_gauss']) - fi_dict_mod['wake']['wake_deflection_parameters']['empirical_gauss']\ ['horizontal_deflection_gain_D'] = 5.0 diff --git a/examples/29_floating_vs_fixedbottom_farm.py b/examples/29_floating_vs_fixedbottom_farm.py new file mode 100644 index 000000000..d7c3dc29d --- /dev/null +++ b/examples/29_floating_vs_fixedbottom_farm.py @@ -0,0 +1,147 @@ +# Copyright 2021 NREL + +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# See https://floris.readthedocs.io for documentation + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from scipy.interpolate import NearestNDInterpolator + +import floris.tools.visualization as wakeviz +from floris.tools import FlorisInterface + + +""" +This example demonstrates the impact of floating on turbine power and thurst +and wake behavior. A floating turbine in FLORIS is defined by including a +`floating_tilt_table` in the turbine input yaml which sets the steady tilt +angle of the turbine based on wind speed. This tilt angle is computed for each +turbine based on effective velocity. This tilt angle is then passed on +to the respective wake model. + +The value of the parameter ref_tilt_cp_ct is the value of tilt at which the +ct/cp curves have been defined. + +With floating_correct_cp_ct_for_tilt True, the difference between the current +tilt as interpolated from the floating tilt table is used to scale the turbine +power and thrust. + +In the example below, a 20-turbine, gridded wind farm is simulated using +the Empirical Gaussian wake model to show the effects of floating turbines on +both turbine power and wake development. + +fi_fixed: Fixed bottom turbine (no tilt variation with wind speed) +fi_floating: Floating turbine (tilt varies with wind speed) +""" + +# Declare the Floris Interface for fixed bottom, provide layout +fi_fixed = FlorisInterface("inputs_floating/emgauss_fixed.yaml") +fi_floating = FlorisInterface("inputs_floating/emgauss_floating.yaml") +x, y = np.meshgrid(np.linspace(0, 4*630., 5), np.linspace(0, 3*630., 4)) +for fi in [fi_fixed, fi_floating]: + fi.reinitialize(layout_x=x.flatten(), layout_y=y.flatten()) + +# Compute a single wind speed and direction, power and wakes +for fi in [fi_fixed, fi_floating]: + fi.reinitialize( + layout_x=x.flatten(), + layout_y=y.flatten(), + wind_speeds=[10], + wind_directions=[270] + ) + fi.calculate_wake() + +powers_fixed = fi_fixed.get_turbine_powers() +powers_floating = fi_floating.get_turbine_powers() +power_difference = powers_floating - powers_fixed + +# Show the power differences +fig, ax = plt.subplots() +ax.set_aspect('equal', adjustable='box') +sc = ax.scatter( + x.flatten(), + y.flatten(), + c=power_difference.flatten()/1000, + cmap="PuOr", + vmin=-30, + vmax=30, + s=200, +) +ax.set_xlabel("x coordinate [m]") +ax.set_ylabel("y coordinate [m]") +ax.set_title("Power increase due to floating for each turbine.") +plt.colorbar(sc, label="Increase (kW)") + +print("Power increase from floating over farm (10m/s, 270deg winds): {0:.2f} kW".\ + format(power_difference.sum()/1000)) + +# Visualize flows (see also 02_visualizations.py) +horizontal_planes = [] +y_planes = [] +for fi in [fi_fixed, fi_floating]: + horizontal_planes.append( + fi.calculate_horizontal_plane( + x_resolution=200, + y_resolution=100, + height=90.0, + ) + ) + y_planes.append( + fi.calculate_y_plane( + x_resolution=200, + z_resolution=100, + crossstream_dist=0.0, + ) + ) + +# Create the plots +fig, ax_list = plt.subplots(2, 1, figsize=(10, 8)) +ax_list = ax_list.flatten() +wakeviz.visualize_cut_plane(horizontal_planes[0], ax=ax_list[0], title="Horizontal") +wakeviz.visualize_cut_plane(y_planes[0], ax=ax_list[1], title="Streamwise profile") +fig.suptitle("Fixed-bottom farm") + +fig, ax_list = plt.subplots(2, 1, figsize=(10, 8)) +ax_list = ax_list.flatten() +wakeviz.visualize_cut_plane(horizontal_planes[1], ax=ax_list[0], title="Horizontal") +wakeviz.visualize_cut_plane(y_planes[1], ax=ax_list[1], title="Streamwise profile") +fig.suptitle("Floating farm") + +# Compute AEP (see 07_calc_aep_from_rose.py for details) +df_wr = pd.read_csv("inputs/wind_rose.csv") +wd_array = np.array(df_wr["wd"].unique(), dtype=float) +ws_array = np.array(df_wr["ws"].unique(), dtype=float) + +wd_grid, ws_grid = np.meshgrid(wd_array, ws_array, indexing="ij") +freq_interp = NearestNDInterpolator(df_wr[["wd", "ws"]], df_wr["freq_val"]) +freq = freq_interp(wd_grid, ws_grid) +freq = freq / np.sum(freq) + +for fi in [fi_fixed, fi_floating]: + fi.reinitialize( + wind_directions=wd_array, + wind_speeds=ws_array, + ) + +# Compute the AEP +aep_fixed = fi_fixed.get_farm_AEP(freq=freq) +aep_floating = fi_floating.get_farm_AEP(freq=freq) +print("Farm AEP (fixed bottom): {:.3f} GWh".format(aep_fixed / 1.0e9)) +print("Farm AEP (floating): {:.3f} GWh".format(aep_floating / 1.0e9)) +print("Floating AEP increase: {0:.3f} GWh ({1:.2f}%)".\ + format((aep_floating - aep_fixed) / 1.0e9, + (aep_floating - aep_fixed)/aep_fixed*100 + ) +) + +plt.show() diff --git a/examples/30_multi_dimensional_cp_ct.py b/examples/30_multi_dimensional_cp_ct.py new file mode 100644 index 000000000..2d2303018 --- /dev/null +++ b/examples/30_multi_dimensional_cp_ct.py @@ -0,0 +1,104 @@ +# Copyright 2023 NREL + +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# See https://floris.readthedocs.io for documentation + + +import numpy as np + +from floris.tools import FlorisInterface + + +""" +This example follows the same setup as example 01 to createa a FLORIS instance and: +1) Makes a two-turbine layout +2) Demonstrates single ws/wd simulations +3) Demonstrates mulitple ws/wd simulations + +with the modification of using a turbine definition that has a multi-dimensional Cp/Ct table. + +In the input file `gch_multi_dim_cp_ct.yaml`, the turbine_type points to a turbine definition, +iea_15MW_floating_multi_dim_cp_ct.yaml located in the turbine_library, +that supplies a multi-dimensional Cp/Ct data file in the form of a .csv file. This .csv file +contains two additional conditions to define Cp/Ct values for: Tp for wave period, and Hs for wave +height. For every combination of Tp and Hs defined, a Cp/Ct/Wind speed table of values is also +defined. It is required for this .csv file to have the last 3 columns be ws, Cp, and Ct. In order +for this table to be used, the flag 'multi_dimensional_cp_ct' must be present and set to true in +the turbine definition. Also of note is the 'velocity_model' must be set to 'multidim_cp_ct' in +the main input file. With both of these values provided, the solver will downselect to use the +interpolant defined at the closest conditions. The user must supply these conditions in the +main input file under the 'flow_field' section, e.g.: + +NOTE: The multi-dimensional Cp/Ct data used in this example is fictional for the purposes of +facilitating this example. The Cp/Ct values for the different wave conditions are scaled +values of the original Cp/Ct data for the IEA 15MW turbine. + +flow_field: + multidim_conditions: + Tp: 2.5 + Hs: 3.01 + +The solver will then use the nearest-neighbor interpolant. These conditions are currently global +and used to select the interpolant at each turbine. + +Also note in the example below that there is a specific method for computing powers when +using turbines with multi-dimensional Cp/Ct data under FlorisInterface, called +'get_turbine_powers_multidim'. The normal 'get_turbine_powers' method will not work. +""" + +# Initialize FLORIS with the given input file via FlorisInterface. +fi = FlorisInterface("inputs/gch_multi_dim_cp_ct.yaml") + +# Convert to a simple two turbine layout +fi.reinitialize(layout_x=[0., 500.], layout_y=[0., 0.]) + +# Single wind speed and wind direction +print('\n========================= Single Wind Direction and Wind Speed =========================') + +# Get the turbine powers assuming 1 wind speed and 1 wind direction +fi.reinitialize(wind_directions=[270.], wind_speeds=[8.0]) + +# Set the yaw angles to 0 +yaw_angles = np.zeros([1,1,2]) # 1 wind direction, 1 wind speed, 2 turbines +fi.calculate_wake(yaw_angles=yaw_angles) + +# Get the turbine powers +turbine_powers = fi.get_turbine_powers_multidim()/1000. +print('The turbine power matrix should be of dimensions 1 WD X 1 WS X 2 Turbines') +print(turbine_powers) +print("Shape: ",turbine_powers.shape) + +# Single wind speed and multiple wind directions +print('\n========================= Single Wind Direction and Multiple Wind Speeds ===============') + + +wind_speeds = np.array([8.0, 9.0, 10.0]) +fi.reinitialize(wind_speeds=wind_speeds) +yaw_angles = np.zeros([1,3,2]) # 1 wind direction, 3 wind speeds, 2 turbines +fi.calculate_wake(yaw_angles=yaw_angles) +turbine_powers = fi.get_turbine_powers_multidim()/1000. +print('The turbine power matrix should be of dimensions 1 WD X 3 WS X 2 Turbines') +print(turbine_powers) +print("Shape: ",turbine_powers.shape) + +# Multiple wind speeds and multiple wind directions +print('\n========================= Multiple Wind Directions and Multiple Wind Speeds ============') + +wind_directions = np.array([260., 270., 280.]) +wind_speeds = np.array([8.0, 9.0, 10.0]) +fi.reinitialize(wind_directions=wind_directions, wind_speeds=wind_speeds) +yaw_angles = np.zeros([1,3,2]) # 1 wind direction, 3 wind speeds, 2 turbines +fi.calculate_wake(yaw_angles=yaw_angles) +turbine_powers = fi.get_turbine_powers_multidim()/1000. +print('The turbine power matrix should be of dimensions 3 WD X 3 WS X 2 Turbines') +print(turbine_powers) +print("Shape: ",turbine_powers.shape) diff --git a/examples/31_multi_dimensional_cp_ct_2Hs.py b/examples/31_multi_dimensional_cp_ct_2Hs.py new file mode 100644 index 000000000..6bbc31d6d --- /dev/null +++ b/examples/31_multi_dimensional_cp_ct_2Hs.py @@ -0,0 +1,76 @@ +# Copyright 2023 NREL + +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# See https://floris.readthedocs.io for documentation + + +import matplotlib.pyplot as plt +import numpy as np + +from floris.tools import FlorisInterface + + +""" +This example follows after example 30 but shows the effect of changing the Hs setting. + +NOTE: The multi-dimensional Cp/Ct data used in this example is fictional for the purposes of +facilitating this example. The Cp/Ct values for the different wave conditions are scaled +values of the original Cp/Ct data for the IEA 15MW turbine. +""" + +# Initialize FLORIS with the given input file via FlorisInterface. +fi = FlorisInterface("inputs/gch_multi_dim_cp_ct.yaml") + +# Make a second FLORIS interface with a different setting for Hs. +# Note the multi-cp-ct file (iea_15MW_multi_dim_Tp_Hs.csv) +# for the turbine model iea_15MW_floating_multi_dim_cp_ct.yaml +# Defines Hs at 1 and 5. +# The value in gch_multi_dim_cp_ct.yaml is 3.01 which will map +# to 5 as the nearer value, so we set the other case to 1 +# for contrast. +fi_dict_mod = fi.floris.as_dict() +fi_dict_mod['flow_field']['multidim_conditions']['Hs'] = 1.0 +fi_hs_1 = FlorisInterface(fi_dict_mod) + +# Set both cases to 3 turbine layout +fi.reinitialize(layout_x=[0., 500., 1000.], layout_y=[0., 0., 0.]) +fi_hs_1.reinitialize(layout_x=[0., 500., 1000.], layout_y=[0., 0., 0.]) + +# Use a sweep of wind speeds +wind_speeds = np.arange(5,20,1.) +fi.reinitialize(wind_directions=[270.], wind_speeds=wind_speeds) +fi_hs_1.reinitialize(wind_directions=[270.], wind_speeds=wind_speeds) + +# Calculate wakes with baseline yaw +fi.calculate_wake() +fi_hs_1.calculate_wake() + +# Collect the turbine powers in kW +turbine_powers = fi.get_turbine_powers_multidim()/1000. +turbine_powers_hs_1 = fi_hs_1.get_turbine_powers_multidim()/1000. + +# Plot the power in each case and the difference in power +fig, axarr = plt.subplots(1,3,sharex=True,figsize=(12,4)) + +for t_idx in range(3): + ax = axarr[t_idx] + ax.plot(wind_speeds, turbine_powers[0,:,t_idx], color='k', label='Hs=3.1 (5)') + ax.plot(wind_speeds, turbine_powers_hs_1[0,:,t_idx], color='r', label='Hs=1.0') + ax.grid(True) + ax.set_xlabel('Wind Speed (m/s)') + ax.set_title(f'Turbine {t_idx}') + +axarr[0].set_ylabel('Power (kW)') +axarr[0].legend() +fig.suptitle('Power of each turbine') + +plt.show() diff --git a/examples/inputs/emgauss.yaml b/examples/inputs/emgauss.yaml index dab5c7940..f984f421d 100644 --- a/examples/inputs/emgauss.yaml +++ b/examples/inputs/emgauss.yaml @@ -65,7 +65,7 @@ wake: empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 - deflection_rate: 15 + deflection_rate: 30 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 @@ -88,8 +88,8 @@ wake: we: 0.05 empirical_gauss: wake_expansion_rates: - - 0.01 - - 0.005 + - 0.023 + - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 diff --git a/examples/inputs/gch.yaml b/examples/inputs/gch.yaml index c30c35c3b..220fafeac 100644 --- a/examples/inputs/gch.yaml +++ b/examples/inputs/gch.yaml @@ -134,6 +134,15 @@ flow_field: # The wind veer as a constant value for all points in the grid. wind_veer: 0.0 + ### + # The conditions that are specified for use with the multi-dimensional Cp/Ct capbility. + # These conditions are external to FLORIS and specified by the user. They are used internally + # through a nearest-neighbor selection process to choose the correct Cp/Ct interpolants + # to use. These conditions are only used with the ``multidim_cp_ct`` velocity deficit model. + multidim_conditions: + Tp: 2.5 + Hs: 3.01 + ### # Configure the wake model. wake: diff --git a/examples/inputs/gch_multi_dim_cp_ct.yaml b/examples/inputs/gch_multi_dim_cp_ct.yaml new file mode 100644 index 000000000..8709fbcc7 --- /dev/null +++ b/examples/inputs/gch_multi_dim_cp_ct.yaml @@ -0,0 +1,92 @@ + +name: GCH multi dimensional Cp/Ct +description: Three turbines using GCH model +floris_version: v3.0.0 + +logging: + console: + enable: true + level: WARNING + file: + enable: false + level: WARNING + +solver: + type: turbine_grid + turbine_grid_points: 3 + +farm: + layout_x: + - 0.0 + - 630.0 + - 1260.0 + layout_y: + - 0.0 + - 0.0 + - 0.0 + turbine_type: + - iea_15MW_floating_multi_dim_cp_ct + +flow_field: + multidim_conditions: + Tp: 2.5 + Hs: 3.01 + air_density: 1.225 + reference_wind_height: -1 # -1 is code for use the hub height + turbulence_intensity: 0.06 + wind_directions: + - 270.0 + wind_shear: 0.12 + wind_speeds: + - 8.0 + wind_veer: 0.0 + +wake: + model_strings: + combination_model: sosfs + deflection_model: gauss + turbulence_model: crespo_hernandez + velocity_model: multidim_cp_ct + + enable_secondary_steering: true + enable_yaw_added_recovery: true + enable_transverse_velocities: true + + wake_deflection_parameters: + gauss: + ad: 0.0 + alpha: 0.58 + bd: 0.0 + beta: 0.077 + dm: 1.0 + ka: 0.38 + kb: 0.004 + jimenez: + ad: 0.0 + bd: 0.0 + kd: 0.05 + + wake_velocity_parameters: + cc: + a_s: 0.179367259 + b_s: 0.0118889215 + c_s1: 0.0563691592 + c_s2: 0.13290157 + a_f: 3.11 + b_f: -0.68 + c_f: 2.41 + alpha_mod: 1.0 + multidim_cp_ct: + alpha: 0.58 + beta: 0.077 + ka: 0.38 + kb: 0.004 + jensen: + we: 0.05 + + wake_turbulence_parameters: + crespo_hernandez: + initial: 0.01 + constant: 0.9 + ai: 0.83 + downstream: -0.25 diff --git a/examples/inputs_floating/emgauss_fixed.yaml b/examples/inputs_floating/emgauss_fixed.yaml new file mode 100644 index 000000000..9d0b23960 --- /dev/null +++ b/examples/inputs_floating/emgauss_fixed.yaml @@ -0,0 +1,105 @@ + +name: Emperical Gaussian +description: Example of single fixed-bottom turbine +floris_version: v3.x + +logging: + console: + enable: true + level: WARNING + file: + enable: false + level: WARNING + +solver: + type: turbine_grid + turbine_grid_points: 3 + +farm: + layout_x: + - 0.0 + - 630.0 + - 1260.0 + layout_y: + - 0.0 + - 0.0 + - 0.0 + turbine_type: + - !include turbine_files/nrel_5MW_fixed.yaml + +flow_field: + air_density: 1.225 + reference_wind_height: -1 # -1 is code for use the hub height + turbulence_intensity: 0.06 + wind_directions: + - 270.0 + wind_shear: 0.12 + wind_speeds: + - 8.0 + wind_veer: 0.0 + +wake: + model_strings: + combination_model: sosfs + deflection_model: empirical_gauss + turbulence_model: wake_induced_mixing + velocity_model: empirical_gauss + + enable_secondary_steering: false + enable_yaw_added_recovery: true + enable_transverse_velocities: false + + wake_deflection_parameters: + gauss: + ad: 0.0 + alpha: 0.58 + bd: 0.0 + beta: 0.077 + dm: 1.0 + ka: 0.38 + kb: 0.004 + jimenez: + ad: 0.0 + bd: 0.0 + kd: 0.05 + empirical_gauss: + horizontal_deflection_gain_D: 3.0 + vertical_deflection_gain_D: -1 + deflection_rate: 30 + mixing_gain_deflection: 0.0 + yaw_added_mixing_gain: 0.0 + + wake_velocity_parameters: + cc: + a_s: 0.179367259 + b_s: 0.0118889215 + c_s1: 0.0563691592 + c_s2: 0.13290157 + a_f: 3.11 + b_f: -0.68 + c_f: 2.41 + alpha_mod: 1.0 + gauss: + alpha: 0.58 + beta: 0.077 + ka: 0.38 + kb: 0.004 + jensen: + we: 0.05 + empirical_gauss: + wake_expansion_rates: + - 0.023 + - 0.008 + breakpoints_D: + - 10 + sigma_0_D: 0.28 + smoothing_length_D: 2.0 + mixing_gain_velocity: 2.0 + wake_turbulence_parameters: + crespo_hernandez: + initial: 0.1 + constant: 0.5 + ai: 0.8 + downstream: -0.32 + wake_induced_mixing: + atmospheric_ti_gain: 0.0 diff --git a/examples/inputs_floating/emgauss_floating.yaml b/examples/inputs_floating/emgauss_floating.yaml new file mode 100644 index 000000000..1fd66d217 --- /dev/null +++ b/examples/inputs_floating/emgauss_floating.yaml @@ -0,0 +1,105 @@ + +name: Emperical Gaussian +description: Example of single floating turbine +floris_version: v3.x + +logging: + console: + enable: true + level: WARNING + file: + enable: false + level: WARNING + +solver: + type: turbine_grid + turbine_grid_points: 3 + +farm: + layout_x: + - 0.0 + - 630.0 + - 1260.0 + layout_y: + - 0.0 + - 0.0 + - 0.0 + turbine_type: + - !include turbine_files/nrel_5MW_floating.yaml + +flow_field: + air_density: 1.225 + reference_wind_height: -1 # -1 is code for use the hub height + turbulence_intensity: 0.06 + wind_directions: + - 270.0 + wind_shear: 0.12 + wind_speeds: + - 8.0 + wind_veer: 0.0 + +wake: + model_strings: + combination_model: sosfs + deflection_model: empirical_gauss + turbulence_model: wake_induced_mixing + velocity_model: empirical_gauss + + enable_secondary_steering: false + enable_yaw_added_recovery: true + enable_transverse_velocities: false + + wake_deflection_parameters: + gauss: + ad: 0.0 + alpha: 0.58 + bd: 0.0 + beta: 0.077 + dm: 1.0 + ka: 0.38 + kb: 0.004 + jimenez: + ad: 0.0 + bd: 0.0 + kd: 0.05 + empirical_gauss: + horizontal_deflection_gain_D: 3.0 + vertical_deflection_gain_D: -1 + deflection_rate: 30 + mixing_gain_deflection: 0.0 + yaw_added_mixing_gain: 0.0 + + wake_velocity_parameters: + cc: + a_s: 0.179367259 + b_s: 0.0118889215 + c_s1: 0.0563691592 + c_s2: 0.13290157 + a_f: 3.11 + b_f: -0.68 + c_f: 2.41 + alpha_mod: 1.0 + gauss: + alpha: 0.58 + beta: 0.077 + ka: 0.38 + kb: 0.004 + jensen: + we: 0.05 + empirical_gauss: + wake_expansion_rates: + - 0.023 + - 0.008 + breakpoints_D: + - 10 + sigma_0_D: 0.28 + smoothing_length_D: 2.0 + mixing_gain_velocity: 2.0 + wake_turbulence_parameters: + crespo_hernandez: + initial: 0.1 + constant: 0.5 + ai: 0.8 + downstream: -0.32 + wake_induced_mixing: + atmospheric_ti_gain: 0.0 diff --git a/examples/inputs_floating/emgauss_floating_fixedtilt15.yaml b/examples/inputs_floating/emgauss_floating_fixedtilt15.yaml index 1bfae562d..dfb4e3155 100644 --- a/examples/inputs_floating/emgauss_floating_fixedtilt15.yaml +++ b/examples/inputs_floating/emgauss_floating_fixedtilt15.yaml @@ -61,7 +61,7 @@ wake: empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 - deflection_rate: 15 + deflection_rate: 30 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 @@ -84,8 +84,8 @@ wake: we: 0.05 empirical_gauss: wake_expansion_rates: - - 0.01 - - 0.005 + - 0.023 + - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 diff --git a/examples/inputs_floating/emgauss_floating_fixedtilt5.yaml b/examples/inputs_floating/emgauss_floating_fixedtilt5.yaml index 04cf30518..67be5dfd3 100644 --- a/examples/inputs_floating/emgauss_floating_fixedtilt5.yaml +++ b/examples/inputs_floating/emgauss_floating_fixedtilt5.yaml @@ -61,7 +61,7 @@ wake: empirical_gauss: horizontal_deflection_gain_D: 3.0 vertical_deflection_gain_D: -1 - deflection_rate: 15 + deflection_rate: 30 mixing_gain_deflection: 0.0 yaw_added_mixing_gain: 0.0 @@ -84,8 +84,8 @@ wake: we: 0.05 empirical_gauss: wake_expansion_rates: - - 0.01 - - 0.005 + - 0.023 + - 0.008 breakpoints_D: - 10 sigma_0_D: 0.28 diff --git a/floris/simulation/__init__.py b/floris/simulation/__init__.py index 12d30aab8..6da5c5ac5 100644 --- a/floris/simulation/__init__.py +++ b/floris/simulation/__init__.py @@ -37,7 +37,21 @@ import floris.logging_manager from .base import BaseClass, BaseModel, State -from .turbine import average_velocity, axial_induction, Ct, power, rotor_effective_velocity, Turbine +from .turbine import ( + average_velocity, + axial_induction, + compute_tilt_angles_for_floating_turbines, + Ct, + power, + rotor_effective_velocity, + TiltTable, + Turbine +) +from .turbine_multi_dim import ( + axial_induction_multidim, + Ct_multidim, + TurbineMultiDimensional +) from .farm import Farm from .grid import ( FlowFieldGrid, @@ -57,6 +71,7 @@ full_flow_sequential_solver, full_flow_turbopark_solver, sequential_solver, + sequential_multidim_solver, turbopark_solver, ) from .floris import Floris diff --git a/floris/simulation/farm.py b/floris/simulation/farm.py index 211032ce1..dc1435a88 100644 --- a/floris/simulation/farm.py +++ b/floris/simulation/farm.py @@ -24,6 +24,7 @@ BaseClass, State, Turbine, + TurbineMultiDimensional, ) from floris.simulation.turbine import compute_tilt_angles_for_floating_turbines from floris.type_dec import ( @@ -247,13 +248,22 @@ def construct_turbine_correct_cp_ct_for_tilt(self): ) def construct_turbine_map(self): - self.turbine_map = [Turbine.from_dict(turb) for turb in self.turbine_definitions] + if 'multi_dimensional_cp_ct' in self.turbine_definitions[0].keys() \ + and self.turbine_definitions[0]['multi_dimensional_cp_ct'] is True: + self.turbine_map = [ + TurbineMultiDimensional.from_dict(turb) for turb in self.turbine_definitions + ] + else: + self.turbine_map = [Turbine.from_dict(turb) for turb in self.turbine_definitions] def construct_turbine_fCts(self): self.turbine_fCts = { turb.turbine_type: turb.fCt_interp for turb in self.turbine_map } + def construct_multidim_turbine_fCts(self): + self.turbine_fCts = [turb.fCt_interp for turb in self.turbine_map] + def construct_turbine_fTilts(self): self.turbine_fTilts = [(turb.turbine_type, turb.fTilt_interp) for turb in self.turbine_map] @@ -262,6 +272,9 @@ def construct_turbine_power_interps(self): turb.turbine_type: turb.power_interp for turb in self.turbine_map } + def construct_multidim_turbine_power_interps(self): + self.turbine_power_interps = [turb.power_interp for turb in self.turbine_map] + def construct_coordinates(self): self.coordinates = np.array([ Vec3([x, y, z]) for x, y, z in zip(self.layout_x, self.layout_y, self.hub_heights) @@ -279,6 +292,38 @@ def expand_farm_properties( sorted_coord_indices, axis=2 ) + if 'multi_dimensional_cp_ct' in self.turbine_definitions[0].keys() \ + and self.turbine_definitions[0]['multi_dimensional_cp_ct'] is True: + wd_dim = np.shape(template_shape)[0] + ws_dim = np.shape(template_shape)[1] + if wd_dim != 1 | ws_dim != 0: + self.turbine_fCts_sorted = np.take_along_axis( + np.reshape( + np.repeat(self.turbine_fCts, wd_dim * ws_dim), + np.shape(template_shape) + ), + sorted_coord_indices, + axis=2 + ) + self.turbine_power_interps_sorted = np.take_along_axis( + np.reshape( + np.repeat(self.turbine_power_interps, wd_dim * ws_dim), + np.shape(template_shape) + ), + sorted_coord_indices, + axis=2 + ) + else: + self.turbine_fCts_sorted = np.take_along_axis( + np.reshape(self.turbine_fCts, np.shape(template_shape)), + sorted_coord_indices, + axis=2 + ) + self.turbine_power_interps_sorted = np.take_along_axis( + np.reshape(self.turbine_power_interps, np.shape(template_shape)), + sorted_coord_indices, + axis=2 + ) self.rotor_diameters_sorted = np.take_along_axis( self.rotor_diameters * template_shape, sorted_coord_indices, @@ -355,6 +400,18 @@ def calculate_tilt_for_eff_velocities(self, rotor_effective_velocities): return tilt_angles def finalize(self, unsorted_indices): + if 'multi_dimensional_cp_ct' in self.turbine_definitions[0].keys() \ + and self.turbine_definitions[0]['multi_dimensional_cp_ct'] is True: + self.turbine_fCts = np.take_along_axis( + self.turbine_fCts_sorted, + unsorted_indices[:,:,:,0,0], + axis=2 + ) + self.turbine_power_interps = np.take_along_axis( + self.turbine_power_interps_sorted, + unsorted_indices[:,:,:,0,0], + axis=2 + ) self.yaw_angles = np.take_along_axis( self.yaw_angles_sorted, unsorted_indices[:,:,:,0,0], diff --git a/floris/simulation/floris.py b/floris/simulation/floris.py index 8ca8854e7..09722a2d7 100644 --- a/floris/simulation/floris.py +++ b/floris/simulation/floris.py @@ -34,6 +34,7 @@ full_flow_turbopark_solver, Grid, PointsGrid, + sequential_multidim_solver, sequential_solver, State, TurbineCubatureGrid, @@ -68,12 +69,26 @@ class Floris(BaseClass): def __attrs_post_init__(self) -> None: + # Configure logging + logging_manager.configure_console_log( + self.logging["console"]["enable"], + self.logging["console"]["level"], + ) + logging_manager.configure_file_log( + self.logging["file"]["enable"], + self.logging["file"]["level"], + ) + self.check_deprecated_inputs() # Initialize farm quanitities that depend on other objects self.farm.construct_turbine_map() - self.farm.construct_turbine_fCts() - self.farm.construct_turbine_power_interps() + if self.wake.model_strings['velocity_model'] == 'multidim_cp_ct': + self.farm.construct_multidim_turbine_fCts() + self.farm.construct_multidim_turbine_power_interps() + else: + self.farm.construct_turbine_fCts() + self.farm.construct_turbine_power_interps() self.farm.construct_hub_heights() self.farm.construct_rotor_diameters() self.farm.construct_turbine_TSRs() @@ -144,16 +159,6 @@ def __attrs_post_init__(self) -> None: self.grid.sorted_coord_indices ) - # Configure logging - logging_manager.configure_console_log( - self.logging["console"]["enable"], - self.logging["console"]["level"], - ) - logging_manager.configure_file_log( - self.logging["file"]["enable"], - self.logging["file"]["level"], - ) - def check_deprecated_inputs(self): """ This function should used when the FLORIS input file changes in order to provide @@ -243,6 +248,13 @@ def steady_state_atmospheric_condition(self): self.grid, self.wake ) + elif vel_model=="multidim_cp_ct": + sequential_multidim_solver( + self.farm, + self.flow_field, + self.grid, + self.wake + ) else: sequential_solver( self.farm, diff --git a/floris/simulation/flow_field.py b/floris/simulation/flow_field.py index a8dbf7393..2781d8c12 100644 --- a/floris/simulation/flow_field.py +++ b/floris/simulation/flow_field.py @@ -41,8 +41,9 @@ class FlowField(BaseClass): air_density: float = field(converter=float) turbulence_intensity: float = field(converter=float) reference_wind_height: float = field(converter=float) - time_series : bool = field(default=False) + time_series: bool = field(default=False) heterogenous_inflow_config: dict = field(default=None) + multidim_conditions: dict = field(default=None) n_wind_speeds: int = field(init=False) n_wind_directions: int = field(init=False) diff --git a/floris/simulation/grid.py b/floris/simulation/grid.py index 8e2325252..80f711f72 100644 --- a/floris/simulation/grid.py +++ b/floris/simulation/grid.py @@ -644,56 +644,6 @@ def set_grid(self) -> None: y_center_of_rotation=self.y_center_of_rotation, ) - # self.sorted_indices = self.x.argsort(axis=2) - # self.unsorted_indices = self.sorted_indices.argsort(axis=2) - - # Put the turbines into the final arrays in their sorted order - # self.x = np.take_along_axis(self.x, self.sorted_indices, axis=2) - # self.y = np.take_along_axis(self.y, self.sorted_indices, axis=2) - # self.z = np.take_along_axis(self.z, self.sorted_indices, axis=2) - - # def finalize(self): - # sorted_indices = self.x.argsort(axis=2) - # unsorted_indices = sorted_indices.argsort(axis=2) - - # # print(self.x) - - # x_coordinates, y_coordinates, _ = self.turbine_coordinates_array.T - - # x_center_of_rotation = (np.min(x_coordinates) + np.max(x_coordinates)) / 2 - # y_center_of_rotation = (np.min(y_coordinates) + np.max(y_coordinates)) / 2 - # # print(x_center_of_rotation) - # # print(y_center_of_rotation) - # # lkj - - # self.x = np.take_along_axis(self.x, self.unsorted_indices, axis=2) - # self.y = np.take_along_axis(self.y, self.unsorted_indices, axis=2) - # self.z = np.take_along_axis(self.z, self.unsorted_indices, axis=2) - # # print(self.x) - - # self.x, self.y, self.z = self._rotated_grid( - # -1 * self.wind_directions, - # (x_center_of_rotation, y_center_of_rotation) - # ) - # TODO figure out how to un-rotate grid for plotting after it has been solved - # pass - - # def _rotated_grid(self, angle, center_of_rotation): - # """ - # Rotate the discrete flow field grid. - # """ - # angle = ((angle - 270) % 360 + 360) % 360 - # # angle = np.reshape(angle, (len(angle), 1, 1)) - # xoffset = self.x - center_of_rotation[0] - # yoffset = self.y - center_of_rotation[1] - # rotated_x = ( - # xoffset * cosd(angle) - yoffset * sind(angle) + center_of_rotation[0] - # ) - # rotated_y = ( - # xoffset * sind(angle) + yoffset * cosd(angle) + center_of_rotation[1] - # ) - # return rotated_x, rotated_y, self.z - @define class PointsGrid(Grid): """ diff --git a/floris/simulation/solver.py b/floris/simulation/solver.py index 5f1767699..6e53a718a 100644 --- a/floris/simulation/solver.py +++ b/floris/simulation/solver.py @@ -27,6 +27,11 @@ TurbineGrid, ) from floris.simulation.turbine import average_velocity +from floris.simulation.turbine_multi_dim import ( + axial_induction_multidim, + Ct_multidim, + multidim_Ct_down_select, +) from floris.simulation.wake import WakeModelManager from floris.simulation.wake_deflection.empirical_gauss import yaw_added_wake_mixing from floris.simulation.wake_deflection.gauss import ( @@ -130,9 +135,9 @@ def sequential_solver( axial_induction_i = axial_induction_i[:, :, 0:1, None, None] turbulence_intensity_i = turbine_turbulence_intensity[:, :, i:i+1] yaw_angle_i = farm.yaw_angles_sorted[:, :, i:i+1, None, None] - hub_height_i = farm.hub_heights_sorted[: ,:, i:i+1, None, None] - rotor_diameter_i = farm.rotor_diameters_sorted[: ,:, i:i+1, None, None] - TSR_i = farm.TSRs_sorted[: ,:, i:i+1, None, None] + hub_height_i = farm.hub_heights_sorted[:, :, i:i+1, None, None] + rotor_diameter_i = farm.rotor_diameters_sorted[:, :, i:i+1, None, None] + TSR_i = farm.TSRs_sorted[:, :, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i @@ -148,7 +153,8 @@ def sequential_solver( hub_height_i, ct_i, TSR_i, - axial_induction_i + axial_induction_i, + flow_field.wind_shear, ) effective_yaw_i += added_yaw @@ -161,7 +167,7 @@ def sequential_solver( turbulence_intensity_i, ct_i, rotor_diameter_i, - **deflection_model_args + **deflection_model_args, ) if model_manager.enable_transverse_velocities: @@ -177,7 +183,8 @@ def sequential_solver( yaw_angle_i, ct_i, TSR_i, - axial_induction_i + axial_induction_i, + flow_field.wind_shear, ) if model_manager.enable_yaw_added_recovery: @@ -204,7 +211,7 @@ def sequential_solver( ct_i, hub_height_i, rotor_diameter_i, - **deficit_model_args + **deficit_model_args, ) wake_field = model_manager.combination_model.function( @@ -217,7 +224,7 @@ def sequential_solver( grid.x_sorted, x_i, rotor_diameter_i, - axial_induction_i + axial_induction_i, ) # Calculate wake overlap for wake-added turbulence (WAT) @@ -232,9 +239,9 @@ def sequential_solver( ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) - * np.array(grid.x_sorted > x_i) - * np.array(np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) - * np.array(grid.x_sorted <= downstream_influence_length + x_i) + * (grid.x_sorted > x_i) + * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) + * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT @@ -291,7 +298,7 @@ def full_flow_sequential_solver( turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_wind_directions, turbine_grid_flow_field.n_wind_speeds, - turbine_grid.sorted_coord_indices + turbine_grid.sorted_coord_indices, ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) @@ -376,7 +383,8 @@ def full_flow_sequential_solver( hub_height_i, ct_i, TSR_i, - axial_induction_i + axial_induction_i, + flow_field.wind_shear, ) effective_yaw_i += added_yaw @@ -389,7 +397,7 @@ def full_flow_sequential_solver( turbulence_intensity_i, ct_i, rotor_diameter_i, - **deflection_model_args + **deflection_model_args, ) if model_manager.enable_transverse_velocities: @@ -405,7 +413,8 @@ def full_flow_sequential_solver( yaw_angle_i, ct_i, TSR_i, - axial_induction_i + axial_induction_i, + flow_field.wind_shear, ) # NOTE: exponential @@ -420,7 +429,7 @@ def full_flow_sequential_solver( ct_i, hub_height_i, rotor_diameter_i, - **deficit_model_args + **deficit_model_args, ) wake_field = model_manager.combination_model.function( @@ -476,10 +485,10 @@ def cc_solver( rotor_diameter_i = farm.rotor_diameters_sorted[: ,:, i:i+1, None, None] mask2 = ( - np.array(grid.x_sorted < x_i + 0.01) - * np.array(grid.x_sorted > x_i - 0.01) - * np.array(grid.y_sorted < y_i + 0.51 * rotor_diameter_i) - * np.array(grid.y_sorted > y_i - 0.51 * rotor_diameter_i) + (grid.x_sorted < x_i + 0.01) + * (grid.x_sorted > x_i - 0.01) + * (grid.y_sorted < y_i + 0.51 * rotor_diameter_i) + * (grid.y_sorted > y_i - 0.51 * rotor_diameter_i) ) turb_inflow_field = ( turb_inflow_field * ~mask2 @@ -536,8 +545,8 @@ def cc_solver( turbulence_intensity_i = turbine_turbulence_intensity[:, :, i:i+1] yaw_angle_i = farm.yaw_angles_sorted[:, :, i:i+1, None, None] - hub_height_i = farm.hub_heights_sorted[: ,:, i:i+1, None, None] - TSR_i = farm.TSRs_sorted[: ,:, i:i+1, None, None] + hub_height_i = farm.hub_heights_sorted[:, :, i:i+1, None, None] + TSR_i = farm.TSRs_sorted[:, :, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i @@ -554,6 +563,7 @@ def cc_solver( turb_Cts[:, :, i:i+1], TSR_i, axial_induction_i, + flow_field.wind_shear, scale=2.0, ) effective_yaw_i += added_yaw @@ -567,7 +577,7 @@ def cc_solver( turbulence_intensity_i, turb_Cts[:, :, i:i+1], rotor_diameter_i, - **deflection_model_args + **deflection_model_args, ) if model_manager.enable_transverse_velocities: @@ -584,7 +594,8 @@ def cc_solver( turb_Cts[:, :, i:i+1], TSR_i, axial_induction_i, - scale=2.0 + flow_field.wind_shear, + scale=2.0, ) if model_manager.enable_yaw_added_recovery: @@ -612,7 +623,7 @@ def cc_solver( farm.rotor_diameters_sorted[:, :, :, None, None], turb_u_wake, Ctmp, - **deficit_model_args + **deficit_model_args, ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( @@ -635,16 +646,17 @@ def cc_solver( ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) - * np.array(grid.x_sorted > x_i) - * np.array(np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) - * np.array(grid.x_sorted <= downstream_influence_length + x_i) + * (grid.x_sorted > x_i) + * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) + * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbine_turbulence_intensity = np.maximum( - np.sqrt( ti_added ** 2 + ambient_turbulence_intensity ** 2 ), + np.sqrt(ti_added ** 2 + ambient_turbulence_intensity ** 2), turbine_turbulence_intensity ) + flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.u_sorted = turb_inflow_field @@ -660,7 +672,7 @@ def full_flow_cc_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid, - model_manager: WakeModelManager + model_manager: WakeModelManager, ) -> None: # Get the flow quantities and turbine performance turbine_grid_farm = copy.deepcopy(farm) @@ -692,7 +704,7 @@ def full_flow_cc_solver( turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_wind_directions, turbine_grid_flow_field.n_wind_speeds, - turbine_grid.sorted_coord_indices + turbine_grid.sorted_coord_indices, ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) @@ -764,9 +776,9 @@ def full_flow_cc_solver( turbulence_intensity_i = \ turbine_grid_flow_field.turbulence_intensity_field_sorted_avg[:, :, i:i+1] yaw_angle_i = turbine_grid_farm.yaw_angles_sorted[:, :, i:i+1, None, None] - hub_height_i = turbine_grid_farm.hub_heights_sorted[: ,:, i:i+1, None, None] - rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[: ,:, i:i+1, None, None] - TSR_i = turbine_grid_farm.TSRs_sorted[: ,:, i:i+1, None, None] + hub_height_i = turbine_grid_farm.hub_heights_sorted[:, :, i:i+1, None, None] + rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[:, :, i:i+1, None, None] + TSR_i = turbine_grid_farm.TSRs_sorted[:, :, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i @@ -783,7 +795,8 @@ def full_flow_cc_solver( turb_Cts[:, :, i:i+1], TSR_i, axial_induction_i, - scale=2.0 + flow_field.wind_shear, + scale=2.0, ) effective_yaw_i += added_yaw @@ -796,7 +809,7 @@ def full_flow_cc_solver( turbulence_intensity_i, turb_Cts[:, :, i:i+1], rotor_diameter_i, - **deflection_model_args + **deflection_model_args, ) if model_manager.enable_transverse_velocities: @@ -813,7 +826,8 @@ def full_flow_cc_solver( turb_Cts[:, :, i:i+1], TSR_i, axial_induction_i, - scale=2.0 + flow_field.wind_shear, + scale=2.0, ) # NOTE: exponential @@ -830,7 +844,7 @@ def full_flow_cc_solver( turbine_grid_farm.rotor_diameters_sorted[:, :, :, None, None], turb_u_wake, Ctmp, - **deficit_model_args + **deficit_model_args, ) flow_field.v_sorted += v_wake @@ -928,9 +942,9 @@ def turbopark_solver( axial_induction_i = axial_induction_i[:, :, 0:1, None, None] turbulence_intensity_i = turbine_turbulence_intensity[:, :, i:i+1] yaw_angle_i = farm.yaw_angles_sorted[:, :, i:i+1, None, None] - hub_height_i = farm.hub_heights_sorted[: ,:, i:i+1, None, None] - rotor_diameter_i = farm.rotor_diameters_sorted[: ,:, i:i+1, None, None] - TSR_i = farm.TSRs_sorted[: ,:, i:i+1, None, None] + hub_height_i = farm.hub_heights_sorted[:, :, i:i+1, None, None] + rotor_diameter_i = farm.rotor_diameters_sorted[:, :, i:i+1, None, None] + TSR_i = farm.TSRs_sorted[:, :, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i @@ -946,7 +960,8 @@ def turbopark_solver( hub_height_i, ct_i, TSR_i, - axial_induction_i + axial_induction_i, + flow_field.wind_shear, ) effective_yaw_i += added_yaw @@ -980,7 +995,7 @@ def turbopark_solver( cubature_weights=grid.cubature_weights ) ct_ii = ct_ii[:, :, 0:1, None, None] - rotor_diameter_ii = farm.rotor_diameters_sorted[: ,:, ii:ii+1, None, None] + rotor_diameter_ii = farm.rotor_diameters_sorted[:, :, ii:ii+1, None, None] deflection_field_ii = model_manager.deflection_model.function( x_ii, @@ -989,10 +1004,10 @@ def turbopark_solver( turbulence_intensity_ii, ct_ii, rotor_diameter_ii, - **deflection_model_args + **deflection_model_args, ) - deflection_field[:,:,ii:ii+1,:,:] = deflection_field_ii[:,:,i:i+1,:,:] + deflection_field[:, :, ii:ii+1, :, :] = deflection_field_ii[:, :, i:i+1, :, :] if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( @@ -1007,7 +1022,8 @@ def turbopark_solver( yaw_angle_i, ct_i, TSR_i, - axial_induction_i + axial_induction_i, + flow_field.wind_shear, ) if model_manager.enable_yaw_added_recovery: @@ -1033,7 +1049,7 @@ def turbopark_solver( farm.rotor_diameters_sorted[:, :, :, None, None], i, deflection_field, - **deficit_model_args + **deficit_model_args, ) wake_field = model_manager.combination_model.function( @@ -1064,9 +1080,9 @@ def turbopark_solver( ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) - * np.array(grid.x_sorted > x_i) - * np.array(np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) - * np.array(grid.x_sorted <= downstream_influence_length + x_i) + * (grid.x_sorted > x_i) + * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) + * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT @@ -1111,7 +1127,6 @@ def full_flow_turbopark_solver( # turbine_grid_farm.construc_turbine_pPs() # turbine_grid_farm.construct_coordinates() - # turbine_grid = TurbineGrid( # turbine_coordinates=turbine_grid_farm.coordinates, # reference_turbine_diameter=turbine_grid_farm.rotor_diameters, @@ -1494,3 +1509,208 @@ def full_flow_empirical_gauss_solver( flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake + + +def sequential_multidim_solver( + farm: Farm, + flow_field: FlowField, + grid: TurbineGrid, + model_manager: WakeModelManager +) -> None: + # Algorithm + # For each turbine, calculate its effect on every downstream turbine. + # For the current turbine, we are calculating the deficit that it adds to downstream turbines. + # Integrate this into the main data structure. + # Move on to the next turbine. + + # <