{"id":837,"date":"2026-10-18T10:36:29","date_gmt":"2026-10-18T02:36:29","guid":{"rendered":"https:\/\/luminvolt.com\/"},"modified":"2026-10-19T12:52:45","modified_gmt":"2026-10-19T04:52:45","slug":"solar-irradiation-calculator","status":"publish","type":"page","link":"https:\/\/luminvolt.com\/es\/solar-irradiation-calculator\/","title":{"rendered":"Calculadora de Radiaci\u00f3n Solar e Inclinaci\u00f3n de Paneles"},"content":{"rendered":"\n<style>\n\/* Page-scoped GeneratePress layout; 837 is replaced after REST draft creation. *\/\nbody.page-id-837 #right-sidebar,body.page-id-837 #left-sidebar,body.page-id-837 .entry-header{display:none!important}\nbody.page-id-837 #primary{float:none!important;width:100%!important}\nbody.page-id-837 #page.site{max-width:none!important}\nbody.page-id-837 .site-content{display:block!important;padding:0!important}\nbody.page-id-837 .site-main,body.page-id-837 .entry-content{margin:0!important}\nbody.page-id-837 .inside-article{padding:0!important;background:transparent!important}\n.lvs{--ink:#102d24;--text:#52665d;--green:#167554;--mint:#e7f2eb;--line:#dbe5df;--pale:#f4f7f3;color:var(--ink);background:var(--pale);font-family:inherit;font-size:15px;line-height:1.6;padding-bottom:1px}\n.lvs,.lvs *{box-sizing:border-box}.lvs [hidden]{display:none!important}.lvs a{color:var(--green);text-decoration:none}.lvs a:hover{text-decoration:underline}.lvs button,.lvs input,.lvs select{font:inherit}.lvs button,.lvs a,.lvs summary,.lvs input,.lvs select{touch-action:manipulation}.lvs :focus-visible{outline:3px solid #d4a248;outline-offset:3px}.lvs h1,.lvs h2,.lvs h3,.lvs p{margin:0}.lvs h1,.lvs h2,.lvs h3{color:var(--ink);font-weight:700;letter-spacing:-.035em;line-height:1.15}.lvs h2{font-size:25px}.lvs h3{font-size:21px}.lvs p{color:var(--text)}.lvs-wrap{width:min(1280px,100% - 80px);margin:auto}.lvs-breadcrumb{display:flex;flex-wrap:wrap;gap:12px;font-size:12px;color:#64786d;padding:25px 0 0}.lvs-breadcrumb a{color:inherit}.lvs-hero{display:grid;grid-template-columns:1.8fr 1fr;align-items:center;gap:32px;padding:46px 0 44px}.lvs-eyebrow{display:flex;align-items:center;gap:9px;font-size:10px!important;font-weight:700;letter-spacing:.13em;color:var(--green)!important;margin-bottom:19px!important}.lvs-eyebrow>span{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--green)}.lvs h1{font-size:clamp(35px,4.1vw,58px);line-height:1.1;max-width:780px}.lvs h1 em{font-style:normal;color:var(--green)}.lvs-intro{max-width:590px;font-size:16px;margin-top:20px!important}.lvs-tags{display:flex;flex-wrap:wrap;gap:10px 20px;margin-top:23px;font-size:11px;color:#566d60}.lvs-tags>span:before{content:'\u2713';margin-right:6px;color:var(--green)}.lvs-tags a{font-weight:600}.lvs-illustration svg{display:block;width:100%;max-width:360px;margin-left:auto}.lvs-workspace{display:grid;grid-template-columns:350px minmax(0,1fr);gap:24px;align-items:start}.lvs-input-panel{padding:27px;background:#fff;border:1px solid var(--line);border-radius:14px;box-shadow:0 5px 20px #102d2405}.lvs-panel-heading{display:flex;align-items:center;gap:14px;margin-bottom:23px}.lvs-step{display:grid;place-items:center;width:40px;height:40px;border-radius:10px;background:var(--mint);color:var(--green);font-size:12px;font-weight:700}.lvs-overline{font-size:10px!important;letter-spacing:.13em;font-weight:700;color:#638071!important;margin-bottom:7px!important}.lvs-input-panel h2{font-size:22px}.lvs fieldset{border:0;padding:0 0 22px;margin:0 0 21px;border-bottom:1px solid var(--line);min-width:0}.lvs legend{font-size:13px;font-weight:700;color:var(--ink);padding:0;margin-bottom:13px}.lvs label{display:block;color:#3c5147;font-size:12px;font-weight:600;margin-bottom:7px}.lvs label>span{font-size:10px;font-weight:400;color:#65766d;margin-left:3px}.lvs input:not([type=range]),.lvs select{display:block;width:100%;height:44px;padding:10px 11px;color:var(--ink);background:#fbfcfa;border:1px solid #cfdbd3;border-radius:6px;box-shadow:none;font-size:13px}.lvs select{padding-right:27px}.lvs input:not([type=range]):focus,.lvs select:focus{border-color:var(--green);background:#fff}.lvs-two{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:13px}.lvs-hint{font-size:10.5px!important;line-height:1.65;margin-top:9px!important;color:#66796c!important}.lvs-label-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.lvs-label-row label{margin:0}.lvs-number-unit{display:flex;align-items:center;gap:5px;font-weight:700}.lvs-number-unit input{width:65px!important;height:36px!important;font-weight:700;text-align:center;padding:5px!important}.lvs input[type=range]{width:100%;margin:13px 0 3px;accent-color:var(--green);cursor:pointer}.lvs-range-labels{display:flex;justify-content:space-between;font-size:10px;color:#6c7c72;margin-bottom:16px}.lvs-advanced{border-bottom:1px solid var(--line);padding-bottom:15px;margin-bottom:21px}.lvs summary{cursor:pointer;list-style:none;font-size:12px;font-weight:600;color:var(--ink)}.lvs summary::-webkit-details-marker{display:none}.lvs-advanced summary,.lvs-monthly summary{display:flex;justify-content:space-between;align-items:center;gap:10px}.lvs summary>span:last-child{font-size:18px;font-weight:400}.lvs-advanced-body{padding-top:15px}.lvs-advanced-body select{margin-bottom:15px}.lvs-primary{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;min-height:48px;padding:12px 15px;border:1px solid var(--green);background:var(--green);color:#fff;border-radius:7px;font-size:12px!important;font-weight:700!important;cursor:pointer;transition:background .15s}.lvs-primary:hover{background:#0c543a}.lvs-primary>span:last-child{font-size:20px}.lvs-primary:disabled{cursor:wait;opacity:.65}.lvs-status{font-size:10.5px!important;text-align:center;margin-top:11px!important;min-height:18px}.lvs-error{margin-top:12px!important;padding:11px 12px;border:1px solid #e6b7a7;background:#fff4ef;color:#863b23!important;font-size:12px;border-radius:6px}.lvs-results{min-width:0;min-height:800px;padding:28px;background:#fff;border:1px solid var(--line);border-radius:14px}.lvs-results-heading{display:flex;align-items:center;justify-content:space-between;gap:18px;margin-bottom:19px}.lvs-results-heading h2{font-size:24px}.lvs-estimate-badge{font-size:10px;white-space:nowrap;color:#667a6b;border:1px solid #d8e4db;border-radius:20px;padding:5px 10px}.lvs-empty{display:flex;min-height:620px;align-items:center;justify-content:center;text-align:center;flex-direction:column;background:linear-gradient(#f8faf7,#fff);border:1px dashed #d6e2d8;border-radius:10px;padding:35px}.lvs-empty svg{width:90px;height:90px;stroke:#8bb49c;stroke-width:1.4;fill:none;margin-bottom:26px}.lvs-empty h3{font-size:29px;line-height:1.25}.lvs-empty p{max-width:340px;font-size:13px;margin:17px 0 24px}.lvs-empty>span{font-size:10px;color:#718378}.lvs-result-context{display:flex;flex-wrap:wrap;justify-content:space-between;gap:7px 15px;font-size:10px;color:#63766a;border-top:1px solid var(--line);padding:14px 0}.lvs-annual{display:flex;justify-content:space-between;align-items:center;gap:15px;background:#113e2e;border-radius:10px;padding:25px 27px;color:#fff}.lvs-annual p{color:#c2d9cb;font-size:12px;margin-bottom:7px}.lvs-annual strong{display:inline-block;font-size:clamp(35px,3.7vw,49px);line-height:1.2;font-weight:600;letter-spacing:-.05em;margin-right:10px;font-variant-numeric:tabular-nums}.lvs-annual>div>div>span{font-size:11px;color:#c2d9cb;white-space:nowrap}.lvs-annual small{display:block;font-size:10px;color:#a7d9b9;margin-top:12px}.lvs-annual svg{flex:0 0 84px;width:84px;height:84px;stroke:#77ae88;stroke-width:1;fill:none}.lvs-metrics{display:grid;grid-template-columns:1fr 1fr 1fr;padding:24px 0}.lvs-metrics>div{padding:0 18px;border-left:1px solid var(--line)}.lvs-metrics>div:first-child{border:0;padding-left:0}.lvs-metrics span{display:block;font-size:10px;color:#64786a;margin-bottom:8px}.lvs-metrics strong{display:block;font-size:28px;line-height:1.2;font-weight:600;letter-spacing:-.04em;font-variant-numeric:tabular-nums}.lvs-metrics small{display:block;font-size:9px;color:#6d7e72;margin-top:8px;line-height:1.5}.lvs-tip{display:flex;gap:10px;align-items:center;background:#eff6f0;padding:12px 13px;border-radius:7px;margin-bottom:27px}.lvs-tip>span{color:var(--green);font-size:23px}.lvs-tip p{font-size:10.5px;line-height:1.6;flex:1}.lvs-tip button{border:0;background:transparent;font-size:10px;color:#156341;font-weight:700;white-space:nowrap;padding:6px 0 6px 7px;cursor:pointer}.lvs-chart-heading{display:flex;align-items:center;justify-content:space-between;gap:15px;margin-bottom:12px}.lvs-chart-heading h3{font-size:19px}.lvs-chart-heading p{font-size:10px;margin-top:6px}.lvs-legend{display:flex;gap:13px;font-size:9px;color:#617567}.lvs-legend>span{display:flex;align-items:center;gap:5px;white-space:nowrap}.lvs-legend i{display:inline-block;width:8px;height:8px;border-radius:2px;background:#278059}.lvs-legend>span:last-child i{background:#bdcfc0}.lvs-chart-panel svg{display:block;width:100%;height:auto;overflow:visible}.lvs-data-footer{display:flex;gap:20px;justify-content:space-between;align-items:center;margin:14px 0 20px}.lvs-data-footer p{font-size:9px;max-width:430px;line-height:1.7}.lvs-text-button{background:none;border:0;color:var(--green);padding:5px 0;font-size:10px!important;font-weight:700!important;white-space:nowrap;cursor:pointer}.lvs-monthly{border-top:1px solid var(--line);padding-top:15px}.lvs table{width:100%;border-collapse:collapse;border:0;margin:0;font-size:11px}.lvs th,.lvs td{padding:11px 8px;border:0;border-bottom:1px solid #e3eae5;text-align:right;white-space:nowrap}.lvs th{color:#627668;font-size:10px;font-weight:600;background:#f7faf6}.lvs th small{font-size:9px;font-weight:400}.lvs th:first-child,.lvs td:first-child{text-align:left}.lvs tbody th{background:transparent;color:var(--ink);font-size:11px}.lvs tfoot th,.lvs tfoot td{font-weight:700;background:#edf5ee;border-bottom:0}.lvs-table-scroll{overflow-x:auto;margin-top:13px}.lvs-manual-table{margin:12px 0 20px}.lvs-manual-table th,.lvs-manual-table td{padding:5px}.lvs-manual-table input{height:34px!important;min-width:0;padding:5px!important}.lvs-manual-table td{width:37%}.lvs-method{display:grid;grid-template-columns:1fr 1.4fr;gap:70px;border-top:1px solid #d6e2d8;padding:55px 0 40px;margin-top:55px}.lvs-method h2{font-size:31px;line-height:1.2}.lvs-method>div>p:not(.lvs-overline){font-size:12px;max-width:370px;margin-top:17px}.lvs-contact{display:inline-flex;gap:16px;align-items:center;font-size:12px;font-weight:700;margin-top:23px}.lvs-method-details details{border-bottom:1px solid #d5e1d8;padding:18px 0}.lvs-method-details details:first-child{padding-top:0}.lvs-method-details summary{font-size:13px;display:flex;gap:16px;align-items:center;line-height:1.6}.lvs-method-details summary>span{font-size:10px!important;color:var(--green);font-weight:500!important;flex:0 0 20px}.lvs-method-details p{font-size:11px;margin:13px 0 0 36px;line-height:1.85}.lvs-method-details a{text-decoration:underline}.lvs-tool-footer{display:flex;justify-content:space-between;gap:15px;padding:24px 0 30px;border-top:1px solid #d6e2d8;font-size:10px;color:#697d6e}.lvs-tool-footer>span{letter-spacing:.1em}.lvs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.lvs-data-overview{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1.5fr);gap:22px;margin:26px 0 30px;padding-top:23px;border-top:1px solid var(--line)}.lvs-data-overview h3{font-size:18px}.lvs-table-subtitle{font-size:10px!important;margin-top:5px!important;margin-bottom:14px!important}.lvs .lvs-monthly{border:0;padding:0;min-width:0}.lvs .lvs-monthly .lvs-table-scroll{margin-top:0}.lvs .lvs-data-overview th,.lvs .lvs-data-overview td{padding:11px 6px;font-variant-numeric:tabular-nums}.lvs .lvs-year-data th{white-space:normal;font-size:10px;line-height:1.5;width:48%}.lvs .lvs-year-data td{font-size:11px}.lvs .lvs-year-data td:last-child{font-size:8px;color:#617567}.lvs .lvs-year-highlight th,.lvs .lvs-year-highlight td{background:#eaf4ec;font-weight:700;color:#125c3e}.lvs .lvs-monthly thead th{font-size:9px}.lvs .lvs-monthly th small{font-size:8px}.lvs .lvs-monthly td,.lvs .lvs-monthly tbody th{font-size:10px}.lvs[data-stale=true] .lvs-annual,.lvs[data-stale=true] .lvs-chart-panel,.lvs[data-stale=true] .lvs-data-overview{opacity:.55}.lvs button:disabled{cursor:not-allowed;opacity:.55}.lvs[data-stale=true] .lvs-estimate-badge{color:#8c562a;border-color:#dfc8b3;background:#fff7ee}\n@media(min-width:851px) and (max-width:1100px){.lvs-data-overview{grid-template-columns:1fr}.lvs .lvs-year-data th{width:60%}}\n@media(max-width:580px){.lvs-data-overview{grid-template-columns:1fr;gap:26px}.lvs-data-overview h3{font-size:20px}.lvs .lvs-year-data th{width:55%;font-size:12px}.lvs .lvs-year-data td{font-size:12px}.lvs .lvs-year-data td:last-child{font-size:10px}.lvs .lvs-monthly table{min-width:345px}.lvs .lvs-monthly td,.lvs .lvs-monthly tbody th{font-size:11px}}\n@media(min-width:1500px){.lvs-workspace{grid-template-columns:365px minmax(0,1fr)}}\n@media(max-width:1100px){.lvs-wrap{width:calc(100% - 48px)}.lvs-workspace{grid-template-columns:310px minmax(0,1fr);gap:18px}.lvs-input-panel,.lvs-results{padding:22px}.lvs-results-heading{align-items:flex-start}.lvs-estimate-badge{font-size:9px;padding:4px 7px}.lvs-results-heading h2{font-size:21px}.lvs-annual{padding:23px 20px}.lvs-annual svg{flex-basis:60px;width:60px;height:60px}.lvs-metrics>div{padding:0 12px}.lvs-metrics strong{font-size:24px}.lvs-chart-heading{align-items:flex-start;flex-direction:column;gap:12px}.lvs-method{gap:40px}.lvs-hero{grid-template-columns:2fr 1fr}}\n@media(max-width:850px){.lvs-workspace{grid-template-columns:1fr}.lvs-hero{grid-template-columns:1fr 210px;gap:15px}.lvs h1{font-size:39px}.lvs-hero .lvs-intro{font-size:14px}.lvs-tags{font-size:10px}.lvs-input-panel{padding:25px}.lvs-input-panel form{display:grid;grid-template-columns:1fr 1fr;gap:0 25px}.lvs-input-panel fieldset{margin-bottom:18px}.lvs-input-panel fieldset:nth-of-type(3){border-bottom:0}.lvs-advanced{align-self:start}.lvs-primary,.lvs-status,.lvs-error,#lvs-manual{grid-column:1\/-1}.lvs-results{min-height:650px;padding:25px}.lvs-empty{min-height:440px}.lvs-chart-heading{flex-direction:row;align-items:center}.lvs-method{grid-template-columns:1fr 1.2fr;gap:32px}.lvs-method h2{font-size:27px}.lvs-annual strong{font-size:47px}.lvs-metrics strong{font-size:30px}.lvs-metrics small{font-size:10px}}\n@media(max-width:580px){.lvs-wrap{width:calc(100% - 30px)}.lvs-breadcrumb{padding-top:18px;font-size:10px}.lvs-hero{display:block;padding:33px 0 30px}.lvs-eyebrow{font-size:8px!important;letter-spacing:.09em;margin-bottom:16px!important}.lvs h1{font-size:clamp(30px,8.5vw,43px);letter-spacing:-.045em}.lvs-intro{margin-top:17px!important}.lvs-tags{gap:7px 12px;font-size:9px;margin-top:17px}.lvs-illustration{display:none}.lvs-workspace{gap:16px}.lvs-input-panel,.lvs-results{padding:21px 18px;border-radius:10px}.lvs-input-panel form{display:block}.lvs-input-panel fieldset:nth-of-type(3){border-bottom:1px solid var(--line)}.lvs-input-panel fieldset{padding-bottom:20px;margin-bottom:20px}.lvs-panel-heading{margin-bottom:22px}.lvs-results-heading{gap:10px}.lvs-results-heading h2{font-size:21px}.lvs-results-heading .lvs-overline{font-size:8px!important}.lvs-estimate-badge{font-size:8px;padding:4px 7px}.lvs-empty{min-height:390px;padding:25px 16px}.lvs-empty h3{font-size:25px}.lvs-empty p{font-size:12px}.lvs-result-context{font-size:9px}.lvs-annual{padding:21px 18px}.lvs-annual strong{font-size:40px}.lvs-annual svg{display:none}.lvs-annual>div>div>span{font-size:10px}.lvs-metrics{padding:21px 0}.lvs-metrics>div{padding:0 10px}.lvs-metrics strong{font-size:23px}.lvs-metrics span{font-size:9px;line-height:1.5}.lvs-metrics small{font-size:8px}.lvs-tip{align-items:flex-start;flex-wrap:wrap;gap:7px;padding:12px}.lvs-tip p{font-size:10px}.lvs-tip button{margin-left:26px;padding:0}.lvs-chart-heading{flex-direction:column;align-items:flex-start;gap:10px}.lvs-chart-heading h3{font-size:20px}.lvs-legend{font-size:9px}.lvs-data-footer{align-items:flex-start;flex-direction:column;gap:8px;margin-bottom:15px}.lvs-data-footer p{font-size:8.5px}.lvs-monthly summary{font-size:11px}.lvs-method{display:block;padding:33px 0 30px;margin-top:35px}.lvs-method h2{font-size:28px}.lvs-method-details{margin-top:28px}.lvs-method-details summary{font-size:12px}.lvs-method-details p{font-size:11px}.lvs-tool-footer{flex-direction:column;gap:10px;font-size:9px;padding-bottom:24px}.lvs-primary{font-size:12px!important}.lvs input:not([type=range]),.lvs select{font-size:16px}.lvs-number-unit input{font-size:16px}}\n@media(prefers-reduced-motion:reduce){.lvs *{transition:none!important;scroll-behavior:auto!important}}\n\n\/* Optional manual electricity prices and gross electricity value. *\/\n.lvs .lvs-tariffs legend{display:flex;align-items:center;gap:9px}.lvs-optional{font-size:8px;font-weight:400;letter-spacing:.04em;color:#738174;text-transform:uppercase}.lvs .lvs-check{display:flex;align-items:center;gap:9px;margin:0;cursor:pointer}.lvs .lvs-check>span{font-size:11px;font-weight:500;color:var(--text);margin:0}.lvs input[type=checkbox]{appearance:auto;display:inline-block;flex:0 0 17px;width:17px;height:17px;padding:0;margin:0;accent-color:var(--green);border-radius:3px}.lvs #lvs-tariff-fields{padding-top:17px}.lvs #lvs-tariff-fields input,.lvs #lvs-tariff-fields select{margin-bottom:13px}.lvs-value-section{padding:23px 20px 20px;margin:26px 0 24px;border:1px solid #d7e6da;border-radius:9px;background:#f7faf5;min-width:0}.lvs-value-heading{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-bottom:18px}.lvs-value-heading h3{font-size:20px}.lvs-value-heading .lvs-overline{font-size:8px!important}.lvs #lvs-value-prompt p{font-size:12px;max-width:520px}.lvs #lvs-value-prompt button{margin-top:7px}.lvs-value-total>div:first-child>span{font-size:11px;color:#536e5a}.lvs-value-total strong{display:block;font-size:clamp(25px,2.8vw,36px);letter-spacing:-.045em;line-height:1.25;margin:8px 0;color:#125535;font-weight:650;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.lvs-value-total small{display:block;font-size:9px;color:#657b6a}.lvs-value-split{display:flex;height:6px;overflow:hidden;border-radius:5px;margin:19px 0;background:#dce7dd}.lvs-value-split span:first-child{background:#26805a}.lvs-value-split span:last-child{background:#aabf85}.lvs-value-breakdown{display:grid;grid-template-columns:1fr 1fr;gap:18px;padding-bottom:18px;border-bottom:1px solid #dce6db}.lvs-value-breakdown>div>span{display:flex;align-items:center;gap:7px;font-size:10px;color:#57705d}.lvs-value-breakdown i{width:7px;height:7px;background:#26805a;border-radius:2px;flex:0 0 7px}.lvs-value-breakdown>div:last-child i{background:#aabf85}.lvs-value-breakdown strong{display:block;font-size:20px;line-height:1.35;letter-spacing:-.025em;font-weight:600;margin:7px 0;color:var(--ink);overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.lvs-value-breakdown small{font-size:9px;color:#677e6b}.lvs .lvs-value-assumptions{font-size:10px;margin-top:14px;line-height:1.8}.lvs[data-stale=true] .lvs-value-section{opacity:.55}\n@media(max-width:580px){.lvs-value-section{padding:19px 15px}.lvs-value-heading{gap:10px;align-items:flex-start}.lvs-value-heading h3{font-size:18px}.lvs-value-heading .lvs-estimate-badge{font-size:8px}.lvs-value-breakdown{gap:14px}.lvs-value-breakdown strong{font-size:18px}.lvs-value-breakdown>div>span{font-size:9px;align-items:flex-start}.lvs-value-breakdown i{margin-top:4px}.lvs-value-total strong{font-size:29px}.lvs-value-total small{font-size:8px}}\n\n.lvs-planning-next{display:flex;justify-content:space-between;align-items:center;gap:18px;padding:18px 20px;margin-top:24px;border:1px solid #d7e4d5;border-radius:8px;background:#f1f6ed}.lvs-planning-next strong{display:block;font-size:13px}.lvs-planning-next p{font-size:10px;margin-top:5px}.lvs-planning-next a{font-size:11px;font-weight:700;white-space:nowrap}@media(max-width:580px){.lvs-planning-next{flex-direction:column;align-items:flex-start;gap:13px}}\n\n<\/style>\n<section class=\"lvs\" id=\"lv-solar-tool\" aria-label=\"Solar resource calculator\">\n  <div class=\"lvs-wrap\">\n    <nav class=\"lvs-breadcrumb\" aria-label=\"Breadcrumb\"><a href=\"\/\">Home<\/a><span>\/<\/span><a href=\"\/solar-project-planning-tools\/\">Solar planning tools<\/a><span>\/<\/span><span>Irradiation<\/span><\/nav>\n    <header class=\"lvs-hero\">\n      <div>\n        <p class=\"lvs-eyebrow\"><span><\/span> LUMINVOLT \u00b7 SOLAR RESOURCE EXPLORER<\/p>\n        <h1>Find your angle.<br><em>See your solar potential.<\/em><\/h1>\n        <p class=\"lvs-intro\">Explore solar radiation, compare panel angles, and estimate annual energy at your project location.<\/p>\n        <div class=\"lvs-tags\"><span>NASA POWER climate data<\/span><span>Fixed-tilt planning<\/span><a href=\"#lvs-method\">Transparent methodology \u2197<\/a><a href=\"\/solar-project-planning-tools\/\">All 9 planning tools \u2197<\/a><\/div>\n      <\/div>\n      <div class=\"lvs-illustration\" aria-hidden=\"true\">\n        <svg viewBox=\"0 0 370 235\" fill=\"none\"><defs><pattern id=\"lvs-panel-grid\" width=\"23\" height=\"24\" patternUnits=\"userSpaceOnUse\"><path d=\"M23 0H0V24\" stroke=\"#7bb8a6\" stroke-width=\".8\"\/><\/pattern><\/defs><circle cx=\"288\" cy=\"53\" r=\"24\" fill=\"#e7b454\"\/><g stroke=\"#d4a248\" stroke-width=\"1.5\"><path d=\"M288 13V5M288 93v8M328 53h8M248 53h-8M317 24l6-6M259 82l-6 6M259 24l-6-6M317 82l6 6\"\/><\/g><path d=\"M30 195H345\" stroke=\"#b5cec3\"\/><ellipse cx=\"178\" cy=\"197\" rx=\"124\" ry=\"11\" fill=\"#d8e7df\"\/><path d=\"M106 185l12-69M230 186l-9-46\" stroke=\"#728f82\" stroke-width=\"7\"\/><path d=\"M70 155l63-95 162 50-64 95z\" fill=\"#163d34\" stroke=\"#658e7c\" stroke-width=\"2\"\/><path d=\"M78 153l58-84 148 44-58 83z\" fill=\"url(#lvs-panel-grid)\"\/><path d=\"M262 76l-32 36M235 64l-18 27\" stroke=\"#d6ac55\" stroke-width=\"1.5\" stroke-dasharray=\"4 5\"\/><path d=\"M258 192a49 49 0 0 0-24-34\" stroke=\"#16815d\" stroke-width=\"1.8\"\/><text x=\"270\" y=\"179\" fill=\"#16815d\" font-family=\"sans-serif\" font-size=\"15\">\u03b2<\/text><circle cx=\"51\" cy=\"43\" r=\"3\" fill=\"#83b5a1\"\/><path d=\"M51 51v37M32 69h38\" stroke=\"#bdd5c8\"\/><text x=\"34\" y=\"225\" fill=\"#698375\" font-family=\"sans-serif\" font-size=\"10\" letter-spacing=\"2\">BETTER PLANNING STARTS WITH THE SUN<\/text><\/svg>\n      <\/div>\n    <\/header>\n\n    <div class=\"lvs-workspace\">\n      <aside class=\"lvs-input-panel\" aria-labelledby=\"lvs-project-title\">\n        <div class=\"lvs-panel-heading\"><span class=\"lvs-step\">01<\/span><div><p class=\"lvs-overline\">SET UP YOUR SITE<\/p><h2 id=\"lvs-project-title\">Project inputs<\/h2><\/div><\/div>\n        <form id=\"lvs-form\">\n          <fieldset><legend>Location<\/legend>\n            <label for=\"lvs-city\">Start with a city<\/label>\n            <select id=\"lvs-city\" name=\"city\"><option value=\"berlin\">Berlin, Germany<\/option><option value=\"beijing\">Beijing, China<\/option><option value=\"madrid\">Madrid, Spain<\/option><option value=\"losangeles\">Los Angeles, USA<\/option><option value=\"dubai\">Dubai, UAE<\/option><option value=\"delhi\">New Delhi, India<\/option><option value=\"sydney\">Sydney, Australia<\/option><option value=\"santiago\">Santiago, Chile<\/option><option value=\"custom\">Custom coordinates<\/option><\/select>\n            <div class=\"lvs-two\"><div><label for=\"lvs-lat\">Latitude <span>\u00b0<\/span><\/label><input id=\"lvs-lat\" name=\"latitude\" type=\"number\" min=\"-65\" max=\"65\" step=\"0.0001\" value=\"52.52\" required><\/div><div><label for=\"lvs-lon\">Longitude <span>\u00b0<\/span><\/label><input id=\"lvs-lon\" name=\"longitude\" type=\"number\" min=\"-180\" max=\"180\" step=\"0.0001\" value=\"13.405\" required><\/div><\/div>\n            <p class=\"lvs-hint\">North \/ east are positive. Model coverage: 65\u00b0S\u201365\u00b0N.<\/p>\n          <\/fieldset>\n          <fieldset><legend>Panel orientation<\/legend>\n            <div class=\"lvs-label-row\"><label for=\"lvs-tilt\">Tilt from horizontal<\/label><div class=\"lvs-number-unit\"><input id=\"lvs-tilt\" name=\"tilt\" type=\"number\" min=\"0\" max=\"90\" step=\"1\" value=\"30\" required><span>\u00b0<\/span><\/div><\/div>\n            <input id=\"lvs-tilt-range\" aria-label=\"Adjust panel tilt\" type=\"range\" min=\"0\" max=\"90\" step=\"1\" value=\"30\"><div class=\"lvs-range-labels\"><span>0\u00b0 \u00b7 Flat<\/span><span>90\u00b0 \u00b7 Vertical<\/span><\/div>\n            <label for=\"lvs-azimuth\">Facing direction <span>azimuth<\/span><\/label><select id=\"lvs-azimuth\" name=\"azimuth\"><option value=\"0\">North \u00b7 0\u00b0<\/option><option value=\"45\">Northeast \u00b7 45\u00b0<\/option><option value=\"90\">East \u00b7 90\u00b0<\/option><option value=\"135\">Southeast \u00b7 135\u00b0<\/option><option value=\"180\" selected>South \u00b7 180\u00b0<\/option><option value=\"225\">Southwest \u00b7 225\u00b0<\/option><option value=\"270\">West \u00b7 270\u00b0<\/option><option value=\"315\">Northwest \u00b7 315\u00b0<\/option><\/select>\n          <\/fieldset>\n          <fieldset><legend>Energy estimate<\/legend><div class=\"lvs-two\"><div><label for=\"lvs-capacity\">System size <span>kWp<\/span><\/label><input id=\"lvs-capacity\" name=\"capacity\" type=\"number\" min=\"0.1\" max=\"1000000\" step=\"0.1\" value=\"10\" required><\/div><div><label for=\"lvs-pr\">Performance ratio <span>%<\/span><\/label><input id=\"lvs-pr\" name=\"pr\" type=\"number\" min=\"10\" max=\"100\" step=\"1\" value=\"80\" required><\/div><\/div><p class=\"lvs-hint\">80% is a starting assumption for combined PV losses. Adjust for your project.<\/p><\/fieldset>\n          <fieldset class=\"lvs-tariffs\"><legend>Electricity prices <span class=\"lvs-optional\">Optional<\/span><\/legend>\n            <label class=\"lvs-check\" for=\"lvs-value-enabled\"><input type=\"checkbox\" id=\"lvs-value-enabled\"><span>Estimate savings &amp; export income<\/span><\/label>\n            <div id=\"lvs-tariff-fields\" hidden>\n              <label for=\"lvs-currency\">Currency<\/label><select id=\"lvs-currency\" disabled><option value=\"EUR\">EUR \u00b7 Euro<\/option><option value=\"USD\">USD \u00b7 US dollar<\/option><option value=\"CNY\">CNY \u00b7 Chinese yuan<\/option><option value=\"GBP\">GBP \u00b7 British pound<\/option><option value=\"AUD\">AUD \u00b7 Australian dollar<\/option><option value=\"CAD\">CAD \u00b7 Canadian dollar<\/option><option value=\"INR\">INR \u00b7 Indian rupee<\/option><option value=\"AED\">AED \u00b7 UAE dirham<\/option><option value=\"CLP\">CLP \u00b7 Chilean peso<\/option><option value=\"LOCAL\">Other local currency<\/option><\/select>\n              <label for=\"lvs-retail-rate\">Electricity purchase price <span>per kWh<\/span><\/label><input id=\"lvs-retail-rate\" type=\"number\" min=\"0\" max=\"1000000\" step=\"any\" placeholder=\"Enter your electricity rate\" disabled>\n              <label for=\"lvs-export-rate\">Surplus export price <span>per kWh<\/span><\/label><input id=\"lvs-export-rate\" type=\"number\" min=\"0\" max=\"1000000\" step=\"any\" placeholder=\"Enter your export rate\" disabled>\n              <label for=\"lvs-self-use\">Self-consumption share <span>%<\/span><\/label><input id=\"lvs-self-use\" type=\"number\" min=\"0\" max=\"100\" step=\"1\" value=\"70\" disabled>\n              <p class=\"lvs-hint\">Share of solar generation used on site. 70% is an editable assumption. Enter both rates in your selected currency; use 0 for unpaid exports.<\/p>\n            <\/div>\n          <\/fieldset>\n          <details class=\"lvs-advanced\"><summary>Data &amp; surface settings <span>+<\/span><\/summary><div class=\"lvs-advanced-body\"><label for=\"lvs-source\">Radiation source<\/label><select id=\"lvs-source\"><option value=\"nasa\">NASA POWER climatology<\/option><option value=\"manual\">Enter monthly radiation<\/option><\/select><label for=\"lvs-albedo\">Ground reflectance <span>0\u20131<\/span><\/label><input id=\"lvs-albedo\" name=\"albedo\" type=\"number\" min=\"0\" max=\"1\" step=\"0.01\" value=\"0.20\" required><p class=\"lvs-hint\">0.20 represents a typical ground-reflection assumption.<\/p><\/div><\/details>\n          <div id=\"lvs-manual\" hidden><p class=\"lvs-hint\">Enter monthly average daily radiation in <strong>kWh\/m\u00b2\/day<\/strong>. Diffuse must not exceed GHI.<\/p><div class=\"lvs-manual-table\"><table><thead><tr><th>Month<\/th><th><abbr title=\"Global horizontal irradiation\">GHI<\/abbr><\/th><th>Diffuse<\/th><\/tr><\/thead><tbody id=\"lvs-manual-rows\"><\/tbody><\/table><\/div><\/div>\n          <button class=\"lvs-primary\" id=\"lvs-calculate\" type=\"submit\"><span>Calculate solar potential<\/span><span aria-hidden=\"true\">\u2197<\/span><\/button>\n          <p class=\"lvs-status\" id=\"lvs-status\" role=\"status\" aria-live=\"polite\">Choose your location, then calculate.<\/p>\n          <p class=\"lvs-error\" id=\"lvs-error\" role=\"alert\" hidden><\/p>\n          <noscript><p class=\"lvs-error\">Enable JavaScript to use this calculator.<\/p><\/noscript>\n        <\/form>\n      <\/aside>\n\n      <section class=\"lvs-results\" id=\"lvs-results\" aria-labelledby=\"lvs-results-title\" aria-busy=\"false\">\n        <div class=\"lvs-results-heading\"><div><p class=\"lvs-overline\">YOUR SOLAR OUTLOOK<\/p><h2 id=\"lvs-results-title\">A clearer view of your site.<\/h2><\/div><span class=\"lvs-estimate-badge\">Planning estimate<\/span><\/div>\n        <div class=\"lvs-empty\" id=\"lvs-empty\"><svg viewBox=\"0 0 100 100\" aria-hidden=\"true\"><circle cx=\"50\" cy=\"50\" r=\"20\"\/><path d=\"M50 12v9m0 58v9M12 50h9m58 0h9M23 23l7 7m40 40 7 7M23 77l7-7m40-40 7-7\"\/><\/svg><h3>Your site has a story.<br>Start with the sunlight.<\/h3><p>Calculate to reveal monthly solar resources, a recommended tilt, and estimated energy production.<\/p><span>No sign-up required \u00b7 Export your results<\/span><\/div>\n        <div id=\"lvs-output\" hidden>\n          <div class=\"lvs-result-context\"><span id=\"lvs-site-label\"><\/span><span id=\"lvs-config-label\"><\/span><\/div>\n          <div class=\"lvs-annual\"><div><p>Annual irradiation on your panels<\/p><div><strong id=\"lvs-annual\">\u2014<\/strong><span>kWh\/m\u00b2\/year<\/span><\/div><small id=\"lvs-gain\"><\/small><\/div><svg viewBox=\"0 0 105 105\" aria-hidden=\"true\"><circle cx=\"52.5\" cy=\"52.5\" r=\"38\"\/><circle cx=\"52.5\" cy=\"52.5\" r=\"25\"\/><path d=\"M53 9v13m0 61v13M9 53h13m61 0h13M22 22l10 10m42 42 10 10M22 84l10-10m42-42 10-10\"\/><\/svg><\/div>\n          <div class=\"lvs-metrics\"><div><span>Estimated energy<\/span><strong id=\"lvs-energy\">\u2014<\/strong><small>kWh\/year \u00b7 at your system size &amp; PR<\/small><\/div><div><span>Recommended tilt<\/span><strong id=\"lvs-best\">\u2014<\/strong><small>Best 1\u00b0 step at your facing direction<\/small><\/div><div><span>Gap to best tilt<\/span><strong id=\"lvs-gap\">\u2014<\/strong><small>Annual irradiation difference<\/small><\/div><\/div>\n          <div class=\"lvs-tip\"><span aria-hidden=\"true\">\u2197<\/span><p id=\"lvs-tip-text\"><\/p><button id=\"lvs-apply\" type=\"button\">Use best tilt<\/button><\/div>\n          <section class=\"lvs-value-section\" aria-labelledby=\"lvs-value-title\"><div class=\"lvs-value-heading\"><div><p class=\"lvs-overline\">YOUR ELECTRICITY VALUE<\/p><h3 id=\"lvs-value-title\">Put a value on your sunlight.<\/h3><\/div><span class=\"lvs-estimate-badge\">Manual tariffs<\/span><\/div>\n            <div id=\"lvs-value-prompt\"><p>Add your electricity prices and expected self-consumption to estimate bill savings and export income.<\/p><button id=\"lvs-add-tariffs\" class=\"lvs-text-button\" type=\"button\">Add electricity prices \u2197<\/button><\/div>\n            <div id=\"lvs-value-output\" hidden>\n              <div class=\"lvs-value-total\"><div><span>Annual electricity value<\/span><strong id=\"lvs-value-total\">\u2014<\/strong><small>Bill savings + export income \u00b7 before costs and taxes<\/small><\/div><div class=\"lvs-value-split\" aria-hidden=\"true\"><span id=\"lvs-self-bar\"><\/span><span id=\"lvs-export-bar\"><\/span><\/div><\/div>\n              <div class=\"lvs-value-breakdown\"><div><span><i><\/i>Self-consumption savings<\/span><strong id=\"lvs-value-savings\">\u2014<\/strong><small id=\"lvs-value-self-energy\"><\/small><\/div><div><span><i><\/i>Surplus export income<\/span><strong id=\"lvs-value-revenue\">\u2014<\/strong><small id=\"lvs-value-export-energy\"><\/small><\/div><\/div>\n              <p class=\"lvs-value-assumptions\" id=\"lvs-value-assumptions\"><\/p><p class=\"lvs-hint\">Flat-rate estimate using your assumed annual self-consumption share. Excludes time-of-use \/ tiered pricing, fixed charges, taxes and system costs.<\/p>\n            <\/div>\n          <\/section>\n          <div class=\"lvs-data-overview\">\n            <section class=\"lvs-year-data\" aria-labelledby=\"lvs-year-title\"><h3 id=\"lvs-year-title\">Annual radiation<\/h3><p class=\"lvs-table-subtitle\">Your site at a glance<\/p><table><caption class=\"lvs-sr-only\">Annual solar resource and panel orientation summary<\/caption><tbody>\n              <tr><th scope=\"row\">Horizontal total<\/th><td id=\"lvs-year-horizontal\">\u2014<\/td><td>kWh\/m\u00b2<\/td><\/tr>\n              <tr><th scope=\"row\">Horizontal diffuse<\/th><td id=\"lvs-year-diffuse\">\u2014<\/td><td>kWh\/m\u00b2<\/td><\/tr>\n              <tr><th scope=\"row\">Best tilt<\/th><td id=\"lvs-year-best-tilt\">\u2014<\/td><td>\u00b0<\/td><\/tr>\n              <tr><th scope=\"row\">At best tilt<\/th><td id=\"lvs-year-best-annual\">\u2014<\/td><td>kWh\/m\u00b2<\/td><\/tr>\n              <tr><th scope=\"row\">Current tilt<\/th><td id=\"lvs-year-tilt\">\u2014<\/td><td>\u00b0<\/td><\/tr>\n              <tr><th scope=\"row\">Facing azimuth<\/th><td id=\"lvs-year-azimuth\">\u2014<\/td><td>\u00b0<\/td><\/tr>\n              <tr class=\"lvs-year-highlight\"><th scope=\"row\">Current panel plane<\/th><td id=\"lvs-year-plane\">\u2014<\/td><td>kWh\/m\u00b2<\/td><\/tr>\n              <tr><th scope=\"row\">Gap to best tilt<\/th><td id=\"lvs-year-gap\">\u2014<\/td><td>%<\/td><\/tr>\n            <\/tbody><\/table><p class=\"lvs-hint\">Best tilt is compared at your selected facing direction.<\/p><\/section>\n            <section class=\"lvs-monthly\" aria-labelledby=\"lvs-monthly-title\"><h3 id=\"lvs-monthly-title\">Monthly radiation<\/h3><p class=\"lvs-table-subtitle\">Monthly totals \u00b7 kWh\/m\u00b2<\/p><div class=\"lvs-table-scroll\" role=\"region\" aria-label=\"Monthly solar results\" tabindex=\"0\"><table><caption class=\"lvs-sr-only\">Monthly irradiation and estimated electricity generation<\/caption><thead><tr><th scope=\"col\">Month<\/th><th scope=\"col\">Horizontal<br><small>kWh\/m\u00b2<\/small><\/th><th scope=\"col\">Diffuse<br><small>kWh\/m\u00b2<\/small><\/th><th scope=\"col\">Panel plane<br><small>kWh\/m\u00b2<\/small><\/th><th scope=\"col\">Energy<br><small>kWh<\/small><\/th><\/tr><\/thead><tbody id=\"lvs-table-body\"><\/tbody><tfoot id=\"lvs-table-foot\"><\/tfoot><\/table><\/div><\/section>\n          <\/div>\n          <section class=\"lvs-chart-panel\" aria-labelledby=\"lvs-chart-title\"><div class=\"lvs-chart-heading\"><div><h3 id=\"lvs-chart-title\">Sunlight, month by month.<\/h3><p>Monthly irradiation \u00b7 kWh\/m\u00b2<\/p><\/div><div class=\"lvs-legend\"><span><i><\/i>Panel plane<\/span><span><i><\/i>Horizontal<\/span><\/div><\/div><div id=\"lvs-chart\"><\/div><\/section>\n          <div class=\"lvs-planning-next\"><div><strong>Continue your project plan.<\/strong><p>Use this yield in the capacity, cost and cash-flow tools.<\/p><\/div><a id=\"lvs-planning-link\" href=\"\/solar-project-planning-tools\/#energy\">Use yield in Planning Tools \u2197<\/a><\/div><div class=\"lvs-data-footer\"><p id=\"lvs-data-caption\"><\/p><button class=\"lvs-text-button\" id=\"lvs-export\" type=\"button\">Download CSV <span aria-hidden=\"true\">\u2193<\/span><\/button><\/div>\n        <\/div>\n      <\/section>\n    <\/div>\n\n    <section class=\"lvs-method\" id=\"lvs-method\" aria-labelledby=\"lvs-method-title\"><div><p class=\"lvs-overline\">KNOW WHAT IS BEHIND THE NUMBERS<\/p><h2 id=\"lvs-method-title\">Built on data.<br>Clear about assumptions.<\/h2><p>Use this tool to compare early design options. A project-specific simulation is the next step before selecting equipment or committing to a yield target.<\/p><a href=\"\/contact\/\" class=\"lvs-contact\">Discuss your mounting design <span>\u2197<\/span><\/a><\/div><div class=\"lvs-method-details\"><details open><summary><span>01<\/span> Where does the sunlight data come from?<\/summary><p><a href=\"https:\/\/power.larc.nasa.gov\/docs\/services\/api\/temporal\/climatology\/\" target=\"_blank\" rel=\"noopener\">NASA POWER<\/a> supplies gridded monthly climate averages of global horizontal and diffuse solar radiation. The returned reference period is shown with each result. These are long-term averages, not a weather forecast or measurements at your exact site. You can also enter your own monthly data.<\/p><\/details><details><summary><span>02<\/span> How are tilt and energy calculated?<\/summary><p>We use an isotropic sky approximation: panel irradiation = direct horizontal irradiation \u00d7 beam geometry ratio + diffuse irradiation \u00d7 (1 + cos \u03b2)\/2 + horizontal irradiation \u00d7 albedo \u00d7 (1 \u2212 cos \u03b2)\/2. The beam ratio integrates sun geometry in 30-minute solar-time steps over a 365-day year, assuming uniform daytime direct-normal irradiance within each month. Monthly daily averages are multiplied by days per month. The recommended tilt searches 0\u201390\u00b0 in 1\u00b0 steps at your chosen azimuth.<\/p><p>Estimated energy (kWh) = panel irradiation (kWh\/m\u00b2) \u00f7 reference irradiance (1 kW\/m\u00b2) \u00d7 installed capacity (kWp) \u00d7 performance ratio. See the <a href=\"https:\/\/pvlib-python.readthedocs.io\/en\/stable\/reference\/generated\/pvlib.irradiance.isotropic.html\" target=\"_blank\" rel=\"noopener\">isotropic diffuse model<\/a>.<\/p><\/details><details><summary><span>03<\/span> How is electricity value estimated?<\/summary><p>Self-consumed energy = estimated generation \u00d7 your self-consumption share. Exported energy = generation \u2212 self-consumed energy. Annual bill savings = self-consumed energy \u00d7 your electricity purchase price. Annual export income = exported energy \u00d7 your surplus export price. Total electricity value is their sum, before costs and taxes.<\/p><p>Prices are entered by you and stay in your selected currency. No supplier tariff or exchange rate is retrieved. The same assumed self-consumption share and flat rates are applied to each month in CSV exports. Hourly matching, time-of-use tariffs, tiered rates and financial payback are outside this estimate.<\/p><\/details><details><summary><span>04<\/span> What is outside this estimate?<\/summary><p>This simplified, monofacial fixed-tilt model does not simulate hourly weather, shading, terrain horizons, row spacing, bifacial gain, tracking, clipping, temperature, snow, or degradation individually. The performance ratio is a user-set combined loss assumption. Results are not a production guarantee, structural calculation, or bankable yield study.<\/p><\/details><\/div><\/section>\n    <footer class=\"lvs-tool-footer\"><span>LUMINVOLT \/ ENGINEERING TOOLS<\/span><a href=\"\/solar-mounting-structural-load-calculator\/\">Next: check mounting loads \u2197<\/a><\/footer>\n  <\/div>\n<\/section>\n\n<script>\n(function (scope) {\n  'use strict';\n  const MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];\n  const DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n  const rad = value => value * Math.PI \/ 180;\n\n  function geometry(latitude, azimuth) {\n    if (!Number.isFinite(latitude) || Math.abs(latitude) > 65) throw new Error('Latitude must be between \u221265\u00b0 and 65\u00b0.');\n    if (!Number.isFinite(azimuth) || azimuth < 0 || azimuth > 360) throw new Error('Azimuth must be between 0\u00b0 and 360\u00b0.');\n    const phi = rad(latitude), az = rad(azimuth);\n    let day = 0;\n    return DAYS.map(days => {\n      const samples = [];\n      let horizontal = 0;\n      for (let d = 0; d < days; d++) {\n        day++;\n        const delta = rad(23.45 * Math.sin(2 * Math.PI * (284 + day) \/ 365));\n        \/\/ Half-hour midpoint integration in local SOLAR time; no clock\/timezone approximation.\n        for (let step = 0; step < 48; step++) {\n          const omega = rad(15 * ((step + 0.5) \/ 2 - 12));\n          const up = Math.sin(phi) * Math.sin(delta) + Math.cos(phi) * Math.cos(delta) * Math.cos(omega);\n          if (up <= 0) continue;\n          const east = -Math.cos(delta) * Math.sin(omega);\n          const north = Math.cos(phi) * Math.sin(delta) - Math.sin(phi) * Math.cos(delta) * Math.cos(omega);\n          samples.push([up, east * Math.sin(az) + north * Math.cos(az)]);\n          horizontal += up;\n        }\n      }\n      return {samples, horizontal};\n    });\n  }\n\n  function validateClimate(climate) {\n    if (!Array.isArray(climate) || climate.length !== 12) throw new Error('Twelve months of radiation data are required.');\n    climate.forEach((row, i) => {\n      if (!Number.isFinite(row.ghi) || !Number.isFinite(row.dhi) || row.ghi <= 0 || row.dhi < 0 || row.dhi > row.ghi || row.ghi > 12) {\n        throw new Error('Invalid radiation data for ' + MONTHS[i] + '. Check GHI and diffuse radiation.');\n      }\n    });\n  }\n\n  function transpose(climate, geom, tilt, albedo) {\n    const beta = rad(tilt), cb = Math.cos(beta), sb = Math.sin(beta);\n    return climate.map((row, i) => {\n      const g = geom[i];\n      const beam = g.samples.reduce((sum, v) => sum + Math.max(0, v[0] * cb + v[1] * sb), 0);\n      const rb = g.horizontal > 0 ? beam \/ g.horizontal : 0;\n      const horizontal = row.ghi * DAYS[i];\n      const diffuse = row.dhi * DAYS[i];\n      const plane = Math.max(0, (horizontal - diffuse) * rb + diffuse * (1 + cb) \/ 2 + horizontal * albedo * (1 - cb) \/ 2);\n      return {month: MONTHS[i], horizontal, diffuse, plane};\n    });\n  }\n\n  function calculate(climate, config) {\n    validateClimate(climate);\n    for (const [name, min, max] of [['tilt', 0, 90], ['albedo', 0, 1], ['capacity', 0.1, 1000000], ['pr', 0.1, 1]]) {\n      if (!Number.isFinite(config[name]) || config[name] < min || config[name] > max) throw new Error(name + ' is outside the supported range.');\n    }\n    const geom = geometry(config.latitude, config.azimuth);\n    const monthly = transpose(climate, geom, config.tilt, config.albedo);\n    const annual = monthly.reduce((sum, row) => sum + row.plane, 0);\n    let bestTilt = 0, bestAnnual = -Infinity;\n    const curve = [];\n    for (let tilt = 0; tilt <= 90; tilt++) {\n      const value = transpose(climate, geom, tilt, config.albedo).reduce((sum, row) => sum + row.plane, 0);\n      curve.push(value);\n      if (value > bestAnnual) {bestAnnual = value; bestTilt = tilt;}\n    }\n    monthly.forEach(row => {row.energy = row.plane * config.capacity * config.pr;});\n    return {\n      monthly, annual, bestTilt, bestAnnual, curve,\n      horizontal: monthly.reduce((sum, row) => sum + row.horizontal, 0),\n      diffuse: monthly.reduce((sum, row) => sum + row.diffuse, 0),\n      energy: annual * config.capacity * config.pr,\n      gap: Math.max(0, (1 - annual \/ bestAnnual) * 100)\n    };\n  }\n\n  function fromPower(data) {\n    const params = data && data.properties && data.properties.parameter;\n    const info = data && data.parameters;\n    if (!params || !info) throw new Error('NASA POWER returned an incomplete response. Please try again.');\n    for (const key of ['ALLSKY_SFC_SW_DWN', 'ALLSKY_SFC_SW_DIFF']) {\n      const units = String(info[key] && info[key].units || '').toLowerCase();\n      if (!units.includes('day') || (!units.includes('kw-hr') && !units.includes('kwh'))) {\n        throw new Error('Unexpected radiation units from NASA POWER.');\n      }\n    }\n    const climate = MONTHS.map(month => ({ghi: params.ALLSKY_SFC_SW_DWN[month], dhi: params.ALLSKY_SFC_SW_DIFF[month]}));\n    validateClimate(climate);\n    return climate;\n  }\n\n  function electricityValue(energy, tariffs) {\n    if (!Number.isFinite(energy) || energy < 0) throw new Error('Energy must be a non-negative number.');\n    if (!tariffs || !Number.isFinite(tariffs.selfUse) || tariffs.selfUse < 0 || tariffs.selfUse > 1) throw new Error('Self-consumption must be between 0% and 100%.');\n    for (const key of ['retailRate', 'exportRate']) {\n      if (!Number.isFinite(tariffs[key]) || tariffs[key] < 0) throw new Error('Electricity rates must be non-negative numbers.');\n    }\n    const selfConsumed = energy * tariffs.selfUse;\n    const exported = energy - selfConsumed;\n    const savings = selfConsumed * tariffs.retailRate;\n    const revenue = exported * tariffs.exportRate;\n    return {selfConsumed, exported, savings, revenue, total: savings + revenue};\n  }\n\n  const api = {MONTHS, DAYS, geometry, transpose, calculate, fromPower, electricityValue};\n  if (typeof module !== 'undefined') {\n    if (module.exports) module.exports = api;\n    else scope.LuminVoltSolarModel = api;\n  } else scope.LuminVoltSolarModel = api;\n})(typeof window !== 'undefined' ? window : globalThis);\n\n(function () {\n  'use strict';\n  const root = document.getElementById('lv-solar-tool');\n  if (!root || root.dataset.ready === 'true') return;\n  root.dataset.ready = 'true';\n  const model = window.LuminVoltSolarModel;\n  const $ = id => root.querySelector('#lvs-' + id);\n  const form = $('form'), cache = new Map();\n\n  const I18N_MONTHS = {\n    zh: ['1\u6708', '2\u6708', '3\u6708', '4\u6708', '5\u6708', '6\u6708', '7\u6708', '8\u6708', '9\u6708', '10\u6708', '11\u6708', '12\u6708'],\n    es: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],\n    fr: ['Jan', 'F\u00e9v', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Ao\u00fbt', 'Sep', 'Oct', 'Nov', 'D\u00e9c'],\n    de: ['Jan', 'Feb', 'M\u00e4r', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],\n    nl: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],\n    en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n  };\n\n  const I18N = {\n    zh: {\n      \"Loading climate data\u2026\": \"\u6b63\u5728\u52a0\u8f09\u6c23\u8c61\u6578\u64da\u2026\",\n      \"Calculate solar potential\": \"\u8a08\u7b97\u592a\u967d\u80fd\u767c\u96fb\u6f5b\u529b\",\n      \"Update required\": \"\u9700\u8981\u91cd\u65b0\u8a08\u7b97\",\n      \"Inputs changed. Calculate again to update the results.\": \"\u8f38\u5165\u53c3\u6578\u5df2\u66f4\u6539\uff0c\u8acb\u91cd\u65b0\u9ede\u64ca\u8a08\u7b97\u4ee5\u66f4\u65b0\u7d50\u679c\u3002\",\n      \"Planning estimate\": \"\u524d\u671f\u898f\u5283\u4f30\u7b97\",\n      \"Your solar outlook.\": \"\u9805\u76ee\u5149\u7167\u8207\u767c\u96fb\u5c55\u671b\",\n      \"Custom location\": \"\u81ea\u5b9a\u7fa9\u4f4d\u7f6e\",\n      \"tilt\": \"\u50be\u89d2\",\n      \"azimuth\": \"\u65b9\u4f4d\u89d2\",\n      \"vs. a horizontal surface\": \"\u76f8\u8f03\u65bc\u6c34\u5e73\u9762\",\n      \"used on site\": \"\u73fe\u5834\u81ea\u767c\u81ea\u7528\",\n      \"exported\": \"\u9918\u96fb\u4e0a\u7db2\",\n      \"Your inputs: purchase \": \"\u60a8\u7684\u8a2d\u5b9a\uff1a\u8cfc\u96fb\u50f9 \",\n      \"\/kWh \u00b7 export \": \"\/kWh \u00b7 \u4e0a\u7db2\u50f9 \",\n      \"% self-consumption.\": \"% \u81ea\u7528\u6bd4\u4f8b\u3002\",\n      \"Your tilt is close to the model\u2019s best annual angle for this facing direction.\": \"\u7576\u524d\u50be\u89d2\u5df2\u975e\u5e38\u63a5\u8fd1\u8a72\u671d\u5411\u4e0b\u7684\u6700\u512a\u5e74\u5ea6\u50be\u89d2\u3002\",\n      \"At \": \"\u7576\u50be\u89d2\u70ba \",\n      \"\u00b0, modeled annual irradiation is \": \"\u00b0 \u6642\uff0c\u540c\u671d\u5411\u4e0b\u6a21\u64ec\u5e74\u7e3d\u8f3b\u5c04\u91cf\u70ba \",\n      \" kWh\/m\u00b2 at the same facing direction.\": \" kWh\/m\u00b2\u3002\",\n      \"Annual\": \"\u5168\u5e74\u5408\u8a08\",\n      \"Estimate ready. Adjust orientation to compare instantly.\": \"\u4f30\u7b97\u5b8c\u6210\u3002\u8abf\u6574\u9762\u677f\u671d\u5411\u89d2\u5ea6\u53ef\u5373\u6642\u6bd4\u5c0d\u7d50\u679c\u3002\",\n      \"Requesting monthly climate averages from NASA POWER\u2026\": \"\u6b63\u5728\u5f9e NASA POWER \u7372\u53d6\u6708\u5ea6\u6c23\u5019\u5e73\u5747\u6578\u64da\u2026\",\n      \"The estimate could not be completed.\": \"\u7121\u6cd5\u5b8c\u6210\u767c\u96fb\u91cf\u4f30\u7b97\u3002\",\n      \"The climate data request timed out.\": \"\u6c23\u8c61\u6578\u64da\u8acb\u6c42\u8d85\u6642\u3002\",\n      \"Could not reach NASA POWER. Check your connection or try again later.\": \"\u7121\u6cd5\u9023\u63a5 NASA POWER \u670d\u52d9\u5668\uff0c\u8acb\u6aa2\u67e5\u7db2\u7d61\u6216\u7a0d\u5f8c\u91cd\u8a66\u3002\",\n      \" You can also enter monthly radiation under Data & surface settings.\": \" \u60a8\u4e5f\u53ef\u4ee5\u5728\u201c\u6578\u64da\u4f86\u6e90\u8207\u53cd\u5c04\u7387\u8a2d\u7f6e\u201d\u4e2d\u624b\u52d5\u586b\u5beb\u5404\u6708\u8f3b\u5c04\u503c\u3002\"\n    },\n    es: {\n      \"Loading climate data\u2026\": \"Cargando datos clim\u00e1ticos\u2026\",\n      \"Calculate solar potential\": \"Calcular potencial solar\",\n      \"Update required\": \"Actualizaci\u00f3n requerida\",\n      \"Inputs changed. Calculate again to update the results.\": \"Par\u00e1metros modificados. Calcule nuevamente para actualizar resultados.\",\n      \"Planning estimate\": \"Estimaci\u00f3n de planificaci\u00f3n\",\n      \"Your solar outlook.\": \"Su panorama solar.\",\n      \"Custom location\": \"Ubicaci\u00f3n personalizada\",\n      \"tilt\": \"inclinaci\u00f3n\",\n      \"azimuth\": \"azimut\",\n      \"vs. a horizontal surface\": \"frente a superficie horizontal\",\n      \"used on site\": \"consumido in situ\",\n      \"exported\": \"inyectado a la red\",\n      \"Your inputs: purchase \": \"Sus datos: compra \",\n      \"\/kWh \u00b7 export \": \"\/kWh \u00b7 inyecci\u00f3n \",\n      \"% self-consumption.\": \"% autoconsumo.\",\n      \"Your tilt is close to the model\u2019s best annual angle for this facing direction.\": \"Su inclinaci\u00f3n es muy cercana al \u00e1ngulo anual \u00f3ptimo para esta orientaci\u00f3n.\",\n      \"At \": \"A \",\n      \"\u00b0, modeled annual irradiation is \": \"\u00b0, la radiaci\u00f3n anual modelada es \",\n      \" kWh\/m\u00b2 at the same facing direction.\": \" kWh\/m\u00b2 con la misma orientaci\u00f3n.\",\n      \"Annual\": \"Anual\",\n      \"Estimate ready. Adjust orientation to compare instantly.\": \"Estimaci\u00f3n lista. Ajuste la orientaci\u00f3n para comparar al instante.\",\n      \"Requesting monthly climate averages from NASA POWER\u2026\": \"Solicitando promedios clim\u00e1ticos a NASA POWER\u2026\",\n      \"The estimate could not be completed.\": \"No se pudo completar la estimaci\u00f3n.\",\n      \"The climate data request timed out.\": \"La solicitud de datos clim\u00e1ticos ha caducado.\",\n      \"Could not reach NASA POWER. Check your connection or try again later.\": \"No se pudo conectar con NASA POWER. Compruebe su conexi\u00f3n o int\u00e9ntelo m\u00e1s tarde.\",\n      \" You can also enter monthly radiation under Data & surface settings.\": \" Tambi\u00e9n puede ingresar la radiaci\u00f3n mensual en Datos clim\u00e1ticos y ajustes de superficie.\"\n    },\n    fr: {\n      \"Loading climate data\u2026\": \"Chargement des donn\u00e9es climatiques\u2026\",\n      \"Calculate solar potential\": \"Calculer le potentiel solaire\",\n      \"Update required\": \"Mise \u00e0 jour requise\",\n      \"Inputs changed. Calculate again to update the results.\": \"Param\u00e8tres modifi\u00e9s. Recalculez pour mettre \u00e0 jour les r\u00e9sultats.\",\n      \"Planning estimate\": \"Estimation de planification\",\n      \"Your solar outlook.\": \"Votre potentiel solaire.\",\n      \"Custom location\": \"Emplacement personnalis\u00e9\",\n      \"tilt\": \"inclinaison\",\n      \"azimuth\": \"azimut\",\n      \"vs. a horizontal surface\": \"par rapport \u00e0 une surface horizontale\",\n      \"used on site\": \"autoconsomm\u00e9 sur site\",\n      \"exported\": \"inject\u00e9 sur le r\u00e9seau\",\n      \"Your inputs: purchase \": \"Vos param\u00e8tres : achat \",\n      \"\/kWh \u00b7 export \": \"\/kWh \u00b7 rachat \",\n      \"% self-consumption.\": \"% d'autoconsommation.\",\n      \"Your tilt is close to the model\u2019s best annual angle for this facing direction.\": \"Votre inclinaison est tr\u00e8s proche de l'angle annuel optimal pour cette orientation.\",\n      \"At \": \"\u00c0 \",\n      \"\u00b0, modeled annual irradiation is \": \"\u00b0, l'irradiation annuelle mod\u00e9lis\u00e9e est de \",\n      \" kWh\/m\u00b2 at the same facing direction.\": \" kWh\/m\u00b2 pour la m\u00eame orientation.\",\n      \"Annual\": \"Annuel\",\n      \"Estimate ready. Adjust orientation to compare instantly.\": \"Estimation pr\u00eate. Ajustez l'orientation pour comparer instantan\u00e9ment.\",\n      \"Requesting monthly climate averages from NASA POWER\u2026\": \"R\u00e9cup\u00e9ration des moyennes climatiques NASA POWER\u2026\",\n      \"The estimate could not be completed.\": \"L'estimation n'a pas pu \u00eatre effectu\u00e9e.\",\n      \"The climate data request timed out.\": \"La requ\u00eate de donn\u00e9es climatiques a expir\u00e9.\",\n      \"Could not reach NASA POWER. Check your connection or try again later.\": \"Impossible de joindre NASA POWER. V\u00e9rifiez votre connexion ou r\u00e9essayez plus tard.\",\n      \" You can also enter monthly radiation under Data & surface settings.\": \" Vous pouvez aussi saisir le rayonnement mensuel dans Param\u00e8tres de donn\u00e9es et de surface.\"\n    },\n    de: {\n      \"Loading climate data\u2026\": \"Klimadaten werden geladen\u2026\",\n      \"Calculate solar potential\": \"Solarpotenzial berechnen\",\n      \"Update required\": \"Aktualisierung erforderlich\",\n      \"Inputs changed. Calculate again to update the results.\": \"Eingaben ge\u00e4ndert. Erneut berechnen, um Ergebnisse zu aktualisieren.\",\n      \"Planning estimate\": \"Planungsabsch\u00e4tzung\",\n      \"Your solar outlook.\": \"Ihre Solaraussichten.\",\n      \"Custom location\": \"Benutzerdefinierter Standort\",\n      \"tilt\": \"Neigung\",\n      \"azimuth\": \"Azimut\",\n      \"vs. a horizontal surface\": \"gg\u00fc. horizontaler Fl\u00e4che\",\n      \"used on site\": \"vor Ort verbraucht\",\n      \"exported\": \"ins Netz eingespeist\",\n      \"Your inputs: purchase \": \"Ihre Eingaben: Bezug \",\n      \"\/kWh \u00b7 export \": \"\/kWh \u00b7 Einspeisung \",\n      \"% self-consumption.\": \"% Eigenverbrauch.\",\n      \"Your tilt is close to the model\u2019s best annual angle for this facing direction.\": \"Ihre Neigung liegt nahe am optimalen Jahreswinkel f\u00fcr diese Himmelsrichtung.\",\n      \"At \": \"Bei \",\n      \"\u00b0, modeled annual irradiation is \": \"\u00b0 betr\u00e4gt die modellierte Jahreseinstrahlung \",\n      \" kWh\/m\u00b2 at the same facing direction.\": \" kWh\/m\u00b2 bei gleicher Ausrichtung.\",\n      \"Annual\": \"J\u00e4hrlich\",\n      \"Estimate ready. Adjust orientation to compare instantly.\": \"Berechnung fertig. Neigung und Azimut anpassen f\u00fcr Sofortvergleich.\",\n      \"Requesting monthly climate averages from NASA POWER\u2026\": \"Monatliche Klimamittelwerte werden von NASA POWER abgefragt\u2026\",\n      \"The estimate could not be completed.\": \"Die Berechnung konnte nicht abgeschlossen werden.\",\n      \"The climate data request timed out.\": \"Zeit\u00fcberschreitung bei der Klimadaten-Abfrage.\",\n      \"Could not reach NASA POWER. Check your connection or try again later.\": \"NASA POWER nicht erreichbar. Bitte Verbindung pr\u00fcfen oder sp\u00e4ter versuchen.\",\n      \" You can also enter monthly radiation under Data & surface settings.\": \" Sie k\u00f6nnen die monatliche Einstrahlung auch unter Daten- & Oberfl\u00e4cheneinstellungen eingeben.\"\n    },\n    nl: {\n      \"Loading climate data\u2026\": \"Klimaatgegevens laden\u2026\",\n      \"Calculate solar potential\": \"Bereken zonnepotentieel\",\n      \"Update required\": \"Bijwerking vereist\",\n      \"Inputs changed. Calculate again to update the results.\": \"Invoer gewijzigd. Bereken opnieuw om resultaten bij te werken.\",\n      \"Planning estimate\": \"Planningsschatting\",\n      \"Your solar outlook.\": \"Uw zonne-energievooruitzichten.\",\n      \"Custom location\": \"Aangepaste locatie\",\n      \"tilt\": \"helling\",\n      \"azimuth\": \"azimut\",\n      \"vs. a horizontal surface\": \"t.o.v. horizontaal vlak\",\n      \"used on site\": \"ter plaatse verbruikt\",\n      \"exported\": \"teruggeleverd\",\n      \"Your inputs: purchase \": \"Uw invoer: afname \",\n      \"\/kWh \u00b7 export \": \"\/kWh \u00b7 teruglevering \",\n      \"% self-consumption.\": \"% eigenverbruik.\",\n      \"Your tilt is close to the model\u2019s best annual angle for this facing direction.\": \"Uw helling ligt dicht bij de optimale jaarlijkse hoek voor deze ori\u00ebntatie.\",\n      \"At \": \"Bij \",\n      \"\u00b0, modeled annual irradiation is \": \"\u00b0 is de gemodelleerde jaarlijkse instraling \",\n      \" kWh\/m\u00b2 at the same facing direction.\": \" kWh\/m\u00b2 bij dezelfde ori\u00ebntatie.\",\n      \"Annual\": \"Jaarlijks\",\n      \"Estimate ready. Adjust orientation to compare instantly.\": \"Schatting gereed. Pas de ori\u00ebntatie aan voor directe vergelijking.\",\n      \"Requesting monthly climate averages from NASA POWER\u2026\": \"Maandelijkse klimaatgemiddelden opvragen bij NASA POWER\u2026\",\n      \"The estimate could not be completed.\": \"De schatting kon niet worden voltooid.\",\n      \"The climate data request timed out.\": \"Time-out bij het ophalen van klimaatgegevens.\",\n      \"Could not reach NASA POWER. Check your connection or try again later.\": \"Kan NASA POWER niet bereiken. Controleer uw verbinding of probeer later opnieuw.\",\n      \" You can also enter monthly radiation under Data & surface settings.\": \" U kunt ook maandelijkse stralingsgegevens invoeren onder Gegevens- & oppervlakte-instellingen.\"\n    },\n    en: {\n      \"Loading climate data\u2026\": \"Loading climate data\u2026\",\n      \"Calculate solar potential\": \"Calculate solar potential\",\n      \"Update required\": \"Update required\",\n      \"Inputs changed. Calculate again to update the results.\": \"Inputs changed. Calculate again to update the results.\",\n      \"Planning estimate\": \"Planning estimate\",\n      \"Your solar outlook.\": \"Your solar outlook.\",\n      \"Custom location\": \"Custom location\",\n      \"tilt\": \"tilt\",\n      \"azimuth\": \"azimuth\",\n      \"vs. a horizontal surface\": \"vs. a horizontal surface\",\n      \"used on site\": \"used on site\",\n      \"exported\": \"exported\",\n      \"Your inputs: purchase \": \"Your inputs: purchase \",\n      \"\/kWh \u00b7 export \": \"\/kWh \u00b7 export \",\n      \"% self-consumption.\": \"% self-consumption.\",\n      \"Your tilt is close to the model\u2019s best annual angle for this facing direction.\": \"Your tilt is close to the model\u2019s best annual angle for this facing direction.\",\n      \"At \": \"At \",\n      \"\u00b0, modeled annual irradiation is \": \"\u00b0, modeled annual irradiation is \",\n      \" kWh\/m\u00b2 at the same facing direction.\": \" kWh\/m\u00b2 at the same facing direction.\",\n      \"Annual\": \"Annual\",\n      \"Estimate ready. Adjust orientation to compare instantly.\": \"Estimate ready. Adjust orientation to compare instantly.\",\n      \"Requesting monthly climate averages from NASA POWER\u2026\": \"Requesting monthly climate averages from NASA POWER\u2026\",\n      \"The estimate could not be completed.\": \"The estimate could not be completed.\",\n      \"The climate data request timed out.\": \"The climate data request timed out.\",\n      \"Could not reach NASA POWER. Check your connection or try again later.\": \"Could not reach NASA POWER. Check your connection or try again later.\",\n      \" You can also enter monthly radiation under Data & surface settings.\": \" You can also enter monthly radiation under Data & surface settings.\"\n    }\n  };\n\n  const langAttr = (document.documentElement.lang || '').toLowerCase();\n  const pathLang = ['zh', 'es', 'fr', 'de', 'nl'].find(function(l) { return location.pathname.indexOf('\/' + l + '\/') === 0 || location.pathname === '\/' + l; });\n  const lang = pathLang || (langAttr.startsWith('zh') ? 'zh' : langAttr.startsWith('es') ? 'es' : langAttr.startsWith('fr') ? 'fr' : langAttr.startsWith('de') ? 'de' : langAttr.startsWith('nl') ? 'nl' : 'en');\n  const locale = { zh: 'zh-TW', es: 'es-ES', fr: 'fr-FR', de: 'de-DE', nl: 'nl-NL', en: 'en-US' }[lang] || 'en-US';\n  const monthLabels = I18N_MONTHS[lang] || I18N_MONTHS.en;\n  const t = k => (I18N[lang] && I18N[lang][k]) || k;\n\n  const places = {\n    berlin: ['Berlin, Germany', 52.52, 13.405], beijing: ['Beijing, China', 39.93, 116.28],\n    madrid: ['Madrid, Spain', 40.4168, -3.7038], losangeles: ['Los Angeles, USA', 34.0522, -118.2437],\n    dubai: ['Dubai, UAE', 25.2048, 55.2708], delhi: ['New Delhi, India', 28.6139, 77.209],\n    sydney: ['Sydney, Australia', -33.8688, 151.2093], santiago: ['Santiago, Chile', -33.4489, -70.6693]\n  };\n\n  let last = null, busy = false, timer;\n  const fmt = (number, digits = 0) => new Intl.NumberFormat(locale, {minimumFractionDigits: digits, maximumFractionDigits: digits}).format(number);\n  const climateKey = cfg => cfg.latitude.toFixed(4) + ',' + cfg.longitude.toFixed(4);\n  const safeCell = value => '\"' + String(value).replace(\/\"\/g, '\"\"') + '\"';\n  const money = (value, currency, digits = 2) => (currency === 'LOCAL' ? 'Local' : currency) + ' ' + fmt(value, digits);\n\n  monthLabels.forEach((name, i) => {\n    const tr = document.createElement('tr');\n    const th = document.createElement('th'); th.scope = 'row'; th.textContent = name; tr.append(th);\n    ['ghi', 'dhi'].forEach(type => {\n      const td = document.createElement('td'), input = document.createElement('input');\n      input.type = 'number'; input.min = type === 'ghi' ? '0.01' : '0'; input.max = '12'; input.step = '0.0001';\n      input.dataset.month = String(i); input.dataset.parameter = type; input.disabled = true;\n      input.setAttribute('aria-label', name + ' ' + (type === 'ghi' ? 'global horizontal' : 'diffuse horizontal') + ' kWh per square metre per day');\n      td.append(input); tr.append(td);\n    });\n    $('manual-rows').append(tr);\n  });\n\n  function config() {\n    return {latitude: Number($('lat').value), longitude: Number($('lon').value), tilt: Number($('tilt').value), azimuth: Number($('azimuth').value), capacity: Number($('capacity').value), pr: Number($('pr').value) \/ 100, albedo: Number($('albedo').value),\n      tariffs: $('value-enabled').checked ? {currency: $('currency').value, selfUse: Number($('self-use').value) \/ 100, retailRate: Number($('retail-rate').value), exportRate: Number($('export-rate').value)} : null};\n  }\n  function manualClimate() {\n    return monthLabels.map((_, i) => ({ghi: Number(root.querySelector('[data-month=\"' + i + '\"][data-parameter=\"ghi\"]').value), dhi: Number(root.querySelector('[data-month=\"' + i + '\"][data-parameter=\"dhi\"]').value)}));\n  }\n  function setBusy(value) {\n    busy = value; $('results').setAttribute('aria-busy', String(value));\n    form.querySelectorAll('input,select,button').forEach(el => {el.disabled = value;});\n    if (!value) {syncSource(); syncTariffs();}\n    $('calculate').firstElementChild.textContent = value ? t('Loading climate data\u2026') : t('Calculate solar potential');\n    $('apply').disabled = value; $('export').disabled = value;\n    $('add-tariffs').disabled = value;\n  }\n  function syncSource() {\n    const manual = $('source').value === 'manual'; $('manual').hidden = !manual;\n    $('manual-rows').querySelectorAll('input').forEach(el => {el.disabled = !manual; el.required = manual;});\n  }\n  function syncTariffs() {\n    const enabled = $('value-enabled').checked;\n    $('tariff-fields').hidden = !enabled;\n    $('tariff-fields').querySelectorAll('input,select').forEach(el => {el.disabled = !enabled;});\n    $('self-use').required = enabled;\n    $('retail-rate').required = enabled && Number($('self-use').value) > 0;\n    $('export-rate').required = enabled && Number($('self-use').value) < 100;\n  }\n  function invalidate() {\n    if (!last) return;\n    $('planning-link').hidden = true; root.dataset.stale = 'true'; $('apply').disabled = true; $('export').disabled = true;\n    $('status').textContent = t('Inputs changed. Calculate again to update the results.');\n    root.querySelector('.lvs-estimate-badge').textContent = t('Update required');\n  }\n  function drawChart(result) {\n    const width = 720, height = 250, left = 38, top = 16, bottom = 215, plotWidth = 670;\n    const max = Math.max(...result.monthly.map(row => Math.max(row.plane, row.horizontal)));\n    const ceiling = Math.max(50, Math.ceil(max \/ 50) * 50), plotHeight = bottom - top;\n    const ns = 'http:\/\/www.w3.org\/2000\/svg';\n    const el = (name, attrs, text) => {const n = document.createElementNS(ns, name); Object.entries(attrs).forEach(([k, v]) => n.setAttribute(k, v)); if (text !== undefined) n.textContent = text; return n;};\n    const svg = el('svg', {viewBox: '0 0 ' + width + ' ' + height, role: 'img', 'aria-labelledby': 'lvs-chart-desc'});\n    svg.append(el('title', {id: 'lvs-chart-desc'}, 'Monthly panel-plane and horizontal irradiation. Exact values are available in the monthly data table below.'));\n    for (let i = 0; i <= 4; i++) {\n      const y = bottom - i * plotHeight \/ 4;\n      svg.append(el('line', {x1: left, y1: y, x2: width - 10, y2: y, stroke: '#e1e9e2', 'stroke-dasharray': i ? '3 5' : '0'}));\n      svg.append(el('text', {x: left - 10, y: y + 3.5, fill: '#718172', 'text-anchor': 'end', 'font-size': 10, 'font-family': 'system-ui,sans-serif'}, String(Math.round(ceiling * i \/ 4))));\n    }\n    result.monthly.forEach((row, i) => {\n      const x = left + (i + .5) * plotWidth \/ 12;\n      for (const [key, offset, color] of [['horizontal', -15, '#bdcfc0'], ['plane', 1, '#278059']]) {\n        const h = row[key] \/ ceiling * plotHeight;\n        const rect = el('rect', {x: x + offset, y: bottom - h, width: 13, height: h, rx: 3, fill: color});\n        rect.append(el('title', {}, monthLabels[i] + ' ' + (key === 'plane' ? 'panel plane' : 'horizontal') + ': ' + fmt(row[key], 1) + ' kWh\/m\u00b2'));\n        svg.append(rect);\n      }\n      svg.append(el('text', {x, y: bottom + 23, fill: '#65796a', 'text-anchor': 'middle', 'font-size': 10, 'font-family': 'system-ui,sans-serif'}, monthLabels[i]));\n    });\n    $('chart').replaceChildren(svg);\n  }\n  function render(climate, cfg, metadata) {\n    const result = model.calculate(climate, cfg);\n    result.economics = cfg.tariffs ? model.electricityValue(result.energy, cfg.tariffs) : null;\n    const city = places[$('city').value];\n    const location = city ? city[0] : t('Custom location');\n    last = {result, cfg, metadata, climate, source: $('source').value, key: climateKey(cfg), location};\n    root.dataset.stale = 'false'; $('error').hidden = true; $('empty').hidden = true; $('output').hidden = false;\n    $('apply').disabled = false; $('export').disabled = false;\n    root.querySelector('.lvs-estimate-badge').textContent = t('Planning estimate');\n    $('results-title').textContent = t('Your solar outlook.');\n    $('site-label').textContent = location + ' \u00b7 ' + cfg.latitude.toFixed(4) + '\u00b0, ' + cfg.longitude.toFixed(4) + '\u00b0';\n    $('config-label').textContent = cfg.tilt + '\u00b0 ' + t('tilt') + ' \u00b7 ' + cfg.azimuth + '\u00b0 ' + t('azimuth') + ' \u00b7 ' + fmt(cfg.capacity, 1) + ' kWp';\n    $('annual').textContent = fmt(result.annual);\n    const gain = (result.annual \/ result.horizontal - 1) * 100;\n    $('gain').textContent = (gain >= 0 ? '+' : '\u2212') + fmt(Math.abs(gain), 1) + '% ' + t('vs. a horizontal surface');\n    const planningQuery = new URLSearchParams({kw: String(cfg.capacity), yield: (result.energy \/ cfg.capacity).toFixed(6), source: $('source').value === 'manual' ? 'manual-irradiation' : 'nasa-power'});\n    if (cfg.tariffs) {planningQuery.set('currency', cfg.tariffs.currency === 'LOCAL' ? 'Local' : cfg.tariffs.currency); planningQuery.set('retail', cfg.tariffs.retailRate); planningQuery.set('export', cfg.tariffs.exportRate); planningQuery.set('self', cfg.tariffs.selfUse * 100);}\n    $('planning-link').href = '\/solar-project-planning-tools\/?' + planningQuery + '#energy'; $('planning-link').hidden = false;\n    $('energy').textContent = fmt(result.energy); $('best').textContent = result.bestTilt + '\u00b0'; $('gap').textContent = fmt(result.gap, 1) + '%';\n    $('value-prompt').hidden = Boolean(result.economics);\n    $('value-output').hidden = !result.economics;\n    if (result.economics) {\n      const e = result.economics, t_tariff = cfg.tariffs;\n      $('value-total').textContent = money(e.total, t_tariff.currency);\n      $('value-savings').textContent = money(e.savings, t_tariff.currency);\n      $('value-revenue').textContent = money(e.revenue, t_tariff.currency);\n      $('value-self-energy').textContent = fmt(e.selfConsumed) + ' kWh\/' + (lang === 'zh' ? '\u5e74' : 'year') + ' ' + t('used on site');\n      $('value-export-energy').textContent = fmt(e.exported) + ' kWh\/' + (lang === 'zh' ? '\u5e74' : 'year') + ' ' + t('exported');\n      $('self-bar').style.width = (t_tariff.selfUse * 100) + '%';\n      $('export-bar').style.width = ((1 - t_tariff.selfUse) * 100) + '%';\n      $('value-assumptions').textContent = t('Your inputs: purchase ') + money(t_tariff.retailRate, t_tariff.currency, 4) + t('\/kWh \u00b7 export ') + money(t_tariff.exportRate, t_tariff.currency, 4) + '\/kWh \u00b7 ' + fmt(t_tariff.selfUse * 100) + t('% self-consumption.');\n    }\n    for (const [id, value, digits] of [\n      ['horizontal', result.horizontal, 1], ['diffuse', result.diffuse, 1],\n      ['best-tilt', result.bestTilt, 0], ['best-annual', result.bestAnnual, 1],\n      ['tilt', cfg.tilt, 0], ['azimuth', cfg.azimuth, 0],\n      ['plane', result.annual, 1], ['gap', result.gap, 1]\n    ]) $('year-' + id).textContent = fmt(value, digits);\n    $('tip-text').textContent = result.gap < .15 ? t('Your tilt is close to the model\u2019s best annual angle for this facing direction.') : t('At ') + result.bestTilt + t('\u00b0, modeled annual irradiation is ') + fmt(result.bestAnnual) + t(' kWh\/m\u00b2 at the same facing direction.');\n    $('apply').hidden = cfg.tilt === result.bestTilt;\n    $('data-caption').textContent = metadata.source + ' \u00b7 ' + metadata.period + '. 365-day model; results rounded for display.';\n    const tbody = document.createDocumentFragment();\n    result.monthly.forEach((row, i) => {\n      const tr = document.createElement('tr'), th = document.createElement('th');\n      th.scope = 'row'; th.textContent = monthLabels[i]; tr.append(th);\n      [row.horizontal, row.diffuse, row.plane, row.energy].forEach(value => {const td = document.createElement('td'); td.textContent = fmt(value, 1); tr.append(td);});\n      tbody.append(tr);\n    });\n    $('table-body').replaceChildren(tbody);\n    const total = document.createElement('tr'), th = document.createElement('th'); th.scope = 'row'; th.textContent = t('Annual'); total.append(th);\n    [result.horizontal, result.diffuse, result.annual, result.energy].forEach(value => {const td = document.createElement('td'); td.textContent = fmt(value, 1); total.append(td);});\n    $('table-foot').replaceChildren(total); drawChart(result);\n    $('status').textContent = t('Estimate ready. Adjust orientation to compare instantly.');\n  }\n\n  async function calculate(event) {\n    if (event) event.preventDefault();\n    if (busy || !form.reportValidity()) return;\n    clearTimeout(timer);\n    const cfg = config(), source = $('source').value;\n    $('error').hidden = true;\n    try {\n      if (source === 'manual') {render(manualClimate(), cfg, {source: 'User-supplied monthly data', period: 'Reference period not provided'}); return;}\n      const key = climateKey(cfg);\n      if (cache.has(key)) {const data = cache.get(key); render(data.climate, cfg, data.metadata); return;}\n      setBusy(true); $('status').textContent = t('Requesting monthly climate averages from NASA POWER\u2026');\n      const controller = new AbortController(), timeout = setTimeout(() => controller.abort(), 25000);\n      let response;\n      try {\n        const query = new URLSearchParams({parameters: 'ALLSKY_SFC_SW_DWN,ALLSKY_SFC_SW_DIFF', community: 'RE', longitude: cfg.longitude.toFixed(4), latitude: cfg.latitude.toFixed(4), format: 'JSON'});\n        response = await fetch('https:\/\/power.larc.nasa.gov\/api\/temporal\/climatology\/point?' + query, {signal: controller.signal, credentials: 'omit', referrerPolicy: 'no-referrer'});\n        if (!response.ok) throw new Error('NASA POWER is temporarily unavailable (HTTP ' + response.status + ').');\n        const data = await response.json();\n        const climate = model.fromPower(data);\n        const metadata = {source: 'NASA POWER', period: data.header && data.header.range ? data.header.range : 'Reference period unavailable'};\n        if (cache.size >= 12) cache.delete(cache.keys().next().value);\n        cache.set(key, {climate, metadata});\n        setBusy(false); render(climate, cfg, metadata);\n      } finally {clearTimeout(timeout);}\n    } catch (error) {\n      setBusy(false); invalidate(); $('status').textContent = t('The estimate could not be completed.');\n      $('error').textContent = (error.name === 'AbortError' ? t('The climate data request timed out.') : error instanceof TypeError ? t('Could not reach NASA POWER. Check your connection or try again later.') : error.message) + t(' You can also enter monthly radiation under Data & surface settings.');\n      $('error').hidden = false;\n    }\n  }\n\n  function inputChanged() {\n    if (busy) return;\n    $('error').hidden = true;\n    if (!last) return;\n    const cfg = config();\n    if (last.source === $('source').value && last.key === climateKey(cfg) && form.checkValidity()) {\n      clearTimeout(timer);\n      timer = setTimeout(() => {\n        try {render($('source').value === 'manual' ? manualClimate() : last.climate, cfg, last.metadata);}\n        catch (_) {invalidate();}\n      }, 90);\n    } else {clearTimeout(timer); invalidate();}\n  }\n\n  $('city').addEventListener('change', () => {\n    const city = places[$('city').value];\n    if (city) {$('lat').value = city[1]; $('lon').value = city[2]; $('azimuth').value = city[1] < 0 ? '0' : '180';}\n    inputChanged();\n  });\n  ['lat', 'lon'].forEach(id => $(id).addEventListener('input', () => {$('city').value = 'custom'; inputChanged();}));\n  $('tilt-range').addEventListener('input', () => {$('tilt').value = $('tilt-range').value; inputChanged();});\n  $('tilt').addEventListener('input', () => {$('tilt-range').value = $('tilt').value; inputChanged();});\n  ['azimuth', 'capacity', 'pr', 'albedo'].forEach(id => $(id).addEventListener('input', inputChanged));\n  $('value-enabled').addEventListener('change', () => {syncTariffs(); inputChanged();});\n  ['currency', 'retail-rate', 'export-rate'].forEach(id => $(id).addEventListener('input', inputChanged));\n  $('self-use').addEventListener('input', () => {syncTariffs(); inputChanged();});\n  $('add-tariffs').addEventListener('click', () => {\n    if (busy) return;\n    $('value-enabled').checked = true; syncTariffs(); $('retail-rate').focus(); inputChanged();\n  });\n  $('manual-rows').addEventListener('input', inputChanged);\n  $('source').addEventListener('change', () => {syncSource(); inputChanged();});\n  $('apply').addEventListener('click', () => {\n    if (!last || root.dataset.stale === 'true') return;\n    clearTimeout(timer);\n    $('tilt').value = last.result.bestTilt; $('tilt-range').value = last.result.bestTilt;\n    render(last.climate, config(), last.metadata);\n  });\n  $('export').addEventListener('click', () => {\n    if (!last || root.dataset.stale === 'true') return;\n    const {cfg, result, metadata, location} = last;\n    const rows = [\n      ['LuminVolt Solar Resource Calculator'], ['Location', location], ['Latitude', cfg.latitude], ['Longitude', cfg.longitude],\n      ['Tilt (deg)', cfg.tilt], ['Azimuth from north clockwise (deg)', cfg.azimuth], ['Capacity (kWp)', cfg.capacity], ['Performance ratio', cfg.pr], ['Albedo', cfg.albedo],\n      ['Source', metadata.source], ['Reference period', metadata.period], ['Model', 'Isotropic sky; 30-minute solar geometry; 365-day year'],\n      ['Recommended tilt at current azimuth (deg)', result.bestTilt], ['Gap to best tilt (%)', result.gap.toFixed(3)],\n      ...(cfg.tariffs ? [\n        ['Tariff source', 'User-entered flat rates; not automatically matched to location'], ['Currency', cfg.tariffs.currency],\n        ['Purchase price per kWh', cfg.tariffs.retailRate], ['Export price per kWh', cfg.tariffs.exportRate], ['Self-consumption (%)', cfg.tariffs.selfUse * 100],\n        ['Annual self-consumed energy (kWh)', result.economics.selfConsumed.toFixed(3)], ['Annual exported energy (kWh)', result.economics.exported.toFixed(3)],\n        ['Annual bill savings', result.economics.savings.toFixed(2)], ['Annual export income', result.economics.revenue.toFixed(2)], ['Annual electricity value before costs and taxes', result.economics.total.toFixed(2)],\n        ['Tariff assumptions', 'Annual self-consumption share applied across months; flat rates; excludes TOU\/tiered pricing, fixed charges, taxes and system costs']\n      ] : []),\n      ['Note', 'Planning estimate. Not a production guarantee or bankable yield study.'], [],\n      ['Month', 'Horizontal (kWh\/m2)', 'Diffuse horizontal (kWh\/m2)', 'Panel plane (kWh\/m2)', 'Estimated energy (kWh)', ...(cfg.tariffs ? ['Self-consumed (kWh)', 'Exported (kWh)', 'Bill savings (' + cfg.tariffs.currency + ')', 'Export income (' + cfg.tariffs.currency + ')', 'Electricity value (' + cfg.tariffs.currency + ')'] : [])],\n      ...result.monthly.map((row, i) => {\n        const e = cfg.tariffs ? model.electricityValue(row.energy, cfg.tariffs) : null;\n        return [monthLabels[i], row.horizontal.toFixed(3), row.diffuse.toFixed(3), row.plane.toFixed(3), row.energy.toFixed(3), ...(e ? [e.selfConsumed.toFixed(3), e.exported.toFixed(3), e.savings.toFixed(2), e.revenue.toFixed(2), e.total.toFixed(2)] : [])];\n      }),\n      ['Annual', result.horizontal.toFixed(3), result.diffuse.toFixed(3), result.annual.toFixed(3), result.energy.toFixed(3), ...(result.economics ? [result.economics.selfConsumed.toFixed(3), result.economics.exported.toFixed(3), result.economics.savings.toFixed(2), result.economics.revenue.toFixed(2), result.economics.total.toFixed(2)] : [])]\n    ];\n    const bom = String.fromCharCode(0xFEFF), crlf = String.fromCharCode(13, 10);\n    const url = URL.createObjectURL(new Blob([bom + rows.map(row => row.map(safeCell).join(',')).join(crlf)], {type: 'text\/csv;charset=utf-8;'}));\n    const link = document.createElement('a'); link.href = url; link.download = 'luminvolt-solar-' + cfg.latitude.toFixed(2) + '-' + cfg.longitude.toFixed(2) + '.csv';\n    document.body.append(link); link.click(); link.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000);\n  });\n  form.addEventListener('submit', calculate);\n  syncSource();\n  syncTariffs();\n})();\n\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Explore la radiaci\u00f3n solar, compare \u00e1ngulos fijos y estime la energ\u00eda anual usando datos de NASA POWER.<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_yoast_wpseo_focuskw":"solar irradiation calculator","_yoast_wpseo_title":"Solar Irradiation & Panel Tilt Calculator | LuminVolt","_yoast_wpseo_metadesc":"Calculate monthly solar irradiation, compare panel tilt angles, and estimate annual PV energy using NASA POWER climate data. Free tool with CSV export.","footnotes":""},"class_list":["post-837","page","type-page","status-publish"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.3 (Yoast SEO v28.7) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Solar Irradiation &amp; Panel Tilt Calculator | LuminVolt<\/title>\n<meta name=\"description\" content=\"Calculate monthly solar irradiation, compare panel tilt angles, and estimate annual PV energy using NASA POWER climate data. Free tool with CSV export.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"http:\/\/luminvolt.com\/es\/solar-irradiation-calculator\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Solar Irradiation &amp; Panel Tilt Calculator\" \/>\n<meta property=\"og:description\" content=\"Calculate monthly solar irradiation, compare panel tilt angles, and estimate annual PV energy using NASA POWER climate data. Free tool with CSV export.\" \/>\n<meta property=\"og:url\" content=\"http:\/\/luminvolt.com\/es\/solar-irradiation-calculator\/\" \/>\n<meta property=\"og:site_name\" content=\"LuminVolt\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-19T04:52:45+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data1\" content=\"4 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"http:\\\/\\\/luminvolt.com\\\/solar-irradiation-calculator\\\/\",\"url\":\"http:\\\/\\\/luminvolt.com\\\/solar-irradiation-calculator\\\/\",\"name\":\"Solar Irradiation & Panel Tilt Calculator | LuminVolt\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/luminvolt.com\\\/#website\"},\"datePublished\":\"2026-10-18T02:36:29+00:00\",\"dateModified\":\"2026-10-19T04:52:45+00:00\",\"description\":\"Calculate monthly solar irradiation, compare panel tilt angles, and estimate annual PV energy using NASA POWER climate data. Free tool with CSV export.\",\"breadcrumb\":{\"@id\":\"http:\\\/\\\/luminvolt.com\\\/solar-irradiation-calculator\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\\\/\\\/luminvolt.com\\\/solar-irradiation-calculator\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\\\/\\\/luminvolt.com\\\/solar-irradiation-calculator\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"http:\\\/\\\/luminvolt.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Solar Irradiation &#038; Panel Tilt Calculator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/luminvolt.com\\\/#website\",\"url\":\"https:\\\/\\\/luminvolt.com\\\/\",\"name\":\"LuminVolt\",\"description\":\"Your Partner in Advanced Solar Mounting Systems &amp; Energy Storage\",\"publisher\":{\"@id\":\"https:\\\/\\\/luminvolt.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/luminvolt.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/luminvolt.com\\\/#organization\",\"name\":\"LuminVolt\",\"url\":\"https:\\\/\\\/luminvolt.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/luminvolt.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/luminvolt.com\\\/wp-content\\\/uploads\\\/2024\\\/12\\\/LuminVolt-02.webp\",\"contentUrl\":\"https:\\\/\\\/luminvolt.com\\\/wp-content\\\/uploads\\\/2024\\\/12\\\/LuminVolt-02.webp\",\"width\":512,\"height\":512,\"caption\":\"LuminVolt\"},\"image\":{\"@id\":\"https:\\\/\\\/luminvolt.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Calculadora de Radiaci\u00f3n Solar e Inclinaci\u00f3n de Paneles | LuminVolt","description":"Calcule la radiaci\u00f3n solar mensual, compare \u00e1ngulos de inclinaci\u00f3n y estime la producci\u00f3n anual fotovoltaica con datos de NASA POWER. Gratuito y con exportaci\u00f3n CSV.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"http:\/\/luminvolt.com\/es\/solar-irradiation-calculator\/","og_locale":"es_ES","og_type":"article","og_title":"Solar Irradiation & Panel Tilt Calculator","og_description":"Calculate monthly solar irradiation, compare panel tilt angles, and estimate annual PV energy using NASA POWER climate data. Free tool with CSV export.","og_url":"http:\/\/luminvolt.com\/es\/solar-irradiation-calculator\/","og_site_name":"LuminVolt","article_modified_time":"2026-10-19T04:52:45+00:00","twitter_card":"summary_large_image","twitter_misc":{"Tiempo de lectura":"4 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"http:\/\/luminvolt.com\/solar-irradiation-calculator\/","url":"http:\/\/luminvolt.com\/solar-irradiation-calculator\/","name":"Calculadora de Radiaci\u00f3n Solar e Inclinaci\u00f3n de Paneles | LuminVolt","isPartOf":{"@id":"https:\/\/luminvolt.com\/#website"},"datePublished":"2026-10-18T02:36:29+00:00","dateModified":"2026-10-19T04:52:45+00:00","description":"Calcule la radiaci\u00f3n solar mensual, compare \u00e1ngulos de inclinaci\u00f3n y estime la producci\u00f3n anual fotovoltaica con datos de NASA POWER. Gratuito y con exportaci\u00f3n CSV.","breadcrumb":{"@id":"http:\/\/luminvolt.com\/solar-irradiation-calculator\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["http:\/\/luminvolt.com\/solar-irradiation-calculator\/"]}]},{"@type":"BreadcrumbList","@id":"http:\/\/luminvolt.com\/solar-irradiation-calculator\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http:\/\/luminvolt.com\/"},{"@type":"ListItem","position":2,"name":"Solar Irradiation &#038; Panel Tilt Calculator"}]},{"@type":"WebSite","@id":"https:\/\/luminvolt.com\/#website","url":"https:\/\/luminvolt.com\/","name":"LuminVolt","description":"Su socio en sistemas avanzados de montaje solar y almacenamiento de energ\u00eda","publisher":{"@id":"https:\/\/luminvolt.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/luminvolt.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/luminvolt.com\/#organization","name":"LuminVolt","url":"https:\/\/luminvolt.com\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/luminvolt.com\/#\/schema\/logo\/image\/","url":"https:\/\/luminvolt.com\/wp-content\/uploads\/2024\/12\/LuminVolt-02.webp","contentUrl":"https:\/\/luminvolt.com\/wp-content\/uploads\/2024\/12\/LuminVolt-02.webp","width":512,"height":512,"caption":"LuminVolt"},"image":{"@id":"https:\/\/luminvolt.com\/#\/schema\/logo\/image\/"}}]}},"yoast_social":{"twitter_title":"","twitter_description":"","twitter_image":""},"_links":{"self":[{"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/pages\/837","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/comments?post=837"}],"version-history":[{"count":9,"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/pages\/837\/revisions"}],"predecessor-version":[{"id":856,"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/pages\/837\/revisions\/856"}],"wp:attachment":[{"href":"https:\/\/luminvolt.com\/es\/wp-json\/wp\/v2\/media?parent=837"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}