3O3IMVFKGSXMVLPLZR3H6MIG3EE5PS4632QE4JMK5SVX2PVSSKDAC W2W6K6FJPLY7VH6YJPJQ7G5SLNFTWT4DM3V4MKGMEQTFRNDE3RIQC LXNHFIWZPD6FZQABBKSOVB2CF4LO4BIA5D6DK7LX2TI63LUKX5LQC GNIA6BK2TG2WOBCSLDBMX4MQ26YMFTIYQFH2XPSKWOBMWSUWOQIAC CF23CLXE24SNMD7YLQ4OHNNVIRIK4FY25Z2Q5N42B7IDLINPE44QC FHLPOB7EDQCT2WVTM7LIJTOLMZYZ6YWZ6RTQNZQFY3RXZP25VWBAC 5Y4B4NC65MPGP63IOCQRQYAFA7SYMIFSKIH63JLXKX7VII3EKKLQC O22SKNQ6LUA45DZ7QFM3RFAMC2X64WMRITU4N5K22CWNGHIYFLJAC NPDXW2TQMOKQL233633EHOJX4VYQGY5IVIHTRFLZSPG32CNX3XXAC 6NRS3ZPPZ6A42IEDMRLQ7LZ5TPPLFQ6AGYVS32TQRGHBIJQW4WBAC DUF23XHRNSXFD7FSO3TSBI7LLUIKBSR6GELWNNJWOZV4YWWFEXCAC 6NB2PU2F64XDSURKYMBECKEN7ZQ63RMDN2GL5CP74YA6HE4K3TTAC 7MCTB5G2W6IB7JQXGO2HCMRHBUYIOKXI4MBODKLAJIK66EMLC5ZQC UWD234SIIBCA45TXXSOSZEWLUF66NFPWET257C3WZQYX3L5EJPCAC PIULOOUAM6OIXXSEIAETIVEEEIQ45DEQXB6QHTDSNVQDH57TGTUAC J47EV2H73JKATJDKBZZNSIGAPC7G5X5DCWZ7YDN55XUNPYPS54TQC AFTBFAXJMQ6SFDHO3X4725EQG64BVQYMTXRPFV6JO6IWCQSI5T3QC UPEYNW7CF54F2XHS3SZ7NDZTFAUGJL4OJ6FHLKC7M6XLXL3M2L4QC KNH5OGCTL3YCOQQPA32M76SGWHGHAPQRIZ7S2CBC5DGIEV32SOYQC P7KZ25R4CNS4PZITRWPBVXRFSLY5TKCOFO6U3SDKX45ITNVU773QC O63TXA57OHFIPUDT4QQBDVXRQA64CUIT7XU4G6MSQPMDRFJPM7IAC O4ALNQWBOXUOZRVT6J7C64I4BNJMHIO44ZWNEWIOUBCLE2C7JBAQC 3WWWPCD6BDUHGDTIQHSVUSETNZCM56FYV5EDE2XIH3BNSXBUIKLQC LX2ZVNAYX2FRCEIJ6XBCBILB3O7YT52AP56QDWCCGLATFCEB7KHQC 5B42RKMDCXT74BWXB7OVBMWVAEZAVFKV6T4I3KBESZIWSNQLPQCAC XTA7M7PSEN3PURH4ARXUMZ3PMEQRVX7FPRQA6LANFVI776LJGELQC QHVOZBISIQ3E3P5F6GGKWVDSXC4OA23HCIH2OMXEVGXKSF5W4APAC DB6B3MEDBTEEIS6KOSJH7L5JFXKDMATJARZ6AR7TF2HCCKTFRZJQC UPNRAOJN5TKYIAQIWTX2MURHUEIPHA2MJKAQOFCDL2PQSGK35OCQC C56DYYC72SFNR6BSKCOVDY2QW6XNP66EUGGKCBROTCFJVQDB4FUAC 5FNNIOUNDS5SG75A4ZR6VY7DS6WU2GNNCOSSHX34RFTRB7LJ3D2QC B2BGMBKLV2KPOSQH4ZVQAT7H4LKATWHCGGDWDLNPDJ5RP7V2FNZAC PI6D6Y5EHSXHSILBYHKRKXWXZ6H6JOR5ZTP44CXBW4LUUIREY7OQC XTPX4PYLPGJAEK52LYOYHLVRCL7N5FFAEY4E3OL23LOWLYPLCILQC E6LXIE7VBUGCR762DFTHYZISEWYFV5E3NM3MGLT5Z7YFQQ5GTYRQC V3QTRVNR5EVDZ2O2WRK7ZCVKSGGYBSNILH44OWXKOJK2ME5JC7UQC FO4ZIKFIZY35GBBNL2V4OCRUSSI2WNZFWSZHAI5BWZENFH5QFG2AC FERLNXU2QISSKOUBSPABB2ERHOWHQRI2GVKDJ352Y4B2FUXBRGRQC 5FPD6WH3Z6WVWVHWSU276I2JF7VRJZJB7GC76UY6MDRFJ7YGUMCQC KSMN5QWFJ5SYMVDJQH7D5S6ZQ2YOPLNLTIYHN7LSMF5Y4U2URDAQC 5R7SS5AWSCPT4WWCS5WUBRFULLXATBZW4FFPBT6S5EYJVEWKL5NQC 3DZWNZUZGMHJH3JL3H7KN746C6XVNBRBFHBSVSB5AH7YYUFVYVDAC YLOKW6ERT2XEQG4T6GBPSBCQWVIHWAKBESCXJBKMQBSDXV3C2ACQC PI4VBCLQL4EGBUJ7B2FA3GH25B22CUIOMOZZ7GNPYCS5OSLIRKPAC TLRGOVFX6KIWQSWRPXS73WW4EHJQ5L5W5NYMO6UM2EF2H3EPAHHQC 64D3E3ZOPPF3UFDFYWGBCKS5NTBR34J27FGESDZSSYMUEPD5ZMLQC Y62XTAEHAC5MNJELNKACRLEVBUU2MKYKFPHB5M2S5X46B4EI2JJQC R5TAB7OOEDIU75AKTUZMU2PDNQTENUYQ3UMWDRVQF7SYHLHEEXKAC "types": ["@cloudflare/workers-types"]}
"types": ["@types/bootstrap"]},"include": [".svelte-kit/ambient.d.ts",".svelte-kit/types/**/$types.d.ts","./vite.config.js","./vite.config.ts","./src/**/*.js","./src/**/*.ts","./src/**/*.svelte"],"exclude": ["./node_modules/**",".svelte-kit/[!ambient.d.ts]**","./src/service-worker.js","./src/service-worker.ts","./src/service-worker.d.ts"]
interface Env {}export class User {env: Envstate: DurableObjectStateconstructor(state: DurableObjectState, env: Env) {this.state = statethis.env = env}async fetch(request: Request): Promise<Response> {console.log(`parsed ${JSON.stringify(json)}`)return await this.fetchCmd(json)}}let id = crypto.randomUUID()}if (x) {x.porteur = json.SaveProjet.porteur || x.porteurx.tel = json.SaveProjet.tel || x.telx.adresse = json.SaveProjet.adresse || x.adressex.simulation = json.SaveProjet.simulation || x.simulation}})resp.id = json.GetProjet}return new Response(JSON.stringify(resp || null), { headers })}}}throw new Error(exhaustiveCheck)}let opts = {}result.push({})}return result}}id: k.slice(3),puissance: v.simulation.puissance,t: v.t,nom: v.nom,let result: Grain.ListProjetsResp = []for (let [k, v] of keys.entries()) {console.log(k, v)if (!v.nom) {await this.state.storage.delete(k)continue}let keys: Map<string,{id: stringsimulation: Grain.SimulationCommunautet: number}> = await this.state.storage.list(opts)nom: stringprefix: 'pi.',async listprojets(_from?: number): Promise<Grain.ListProjetsResp> {const exhaustiveCheck: never = jsonreturn new Response('null', { headers })if ('DelProjet' in json) {let t: undefined | { t: string } = await this.state.storage.get(`pi.${json.DelProjet.id}`)if (t) {await this.state.storage.delete(`pi.${json.DelProjet.id}`)console.log('resp', JSON.stringify(resp))if (resp) {if ('GetProjet' in json) {let resp = <Grain.GetProjetResp>(await this.state.storage.get(`pi.${json.GetProjet}`)return new Response('null', { headers })await this.state.storage.put(`pi.${json.SaveProjet.id}`, x)} else {console.log('no such project')x.nom = json.SaveProjet.nom || x.nomif ('SaveProjet' in json) {let x: undefined | Grain.GetProjetResp =await this.state.storage.get(`pi.${json.SaveProjet.id}`)porteur: json.CreateProjet.porteur,tel: json.CreateProjet.tel,adresse: json.CreateProjet.adresse,simulation: json.CreateProjet.simulation,t: Date.now(),})console.log('saved', id)return new Response(JSON.stringify(id), { headers })await this.state.storage.put(`pi.${id}`, {nom: json.CreateProjet.nom,if ('CreateProjet' in json) {// Grainif ('ListProjets' in json) {return new Response(JSON.stringify(await this.listprojets()), {headers,})async fetchCmd(json: Grain.Cmd): Promise<Response> {let headers = new Headers()headers.set('Content-Type', 'application/json')let json = <Grain.Cmd>await request.json()console.log('parsing json')export default {}import type { Grain } from './helpers'
<script lang="ts"></script><div class="form-check form-switch"><label class="form-check-label" for="annuel">Présentation annuelle</label></div>{#if annuel}<div class="my-3"><thead><tr><th>Capital restant dû</th><th>Principal</th><th>Intérêts</th></tr></thead><tbody>{#each remboursementAnnuel as r, periode}<tr><td>{periode}</td><td>{crdAnnuel[periode].toFixed(0)}</td><td>{r.toFixed(0)}</td><td>{interetsAnnuels[periode].toFixed(0)}</td></tr>{/each}</tbody></table></div>{:else}<div class="my-3"><thead><tr><th>Mois</th><th>Capital restant dû</th><th>Principal</th><th>Intérêts</th></tr></thead><tbody>{#each remboursement as r, periode}<tr><td>{periode}</td><td>{crd[periode].toFixed(0)}</td><td>{r.toFixed(0)}</td></td></tr>{/each}</tbody></table></div>{/if}</style><style lang="scss">table {font-size: 90%;}th {font-size: 1rem;font-weight: bold;}td {padding: 5px 5px;padding: 5px 7px 5px 10px;text-align: end;border: none;}<td>{#if periode > 0}{interets[periode].toFixed(0)}{/if}<table class="table table-striped border-0 d-inline-block"><th>Année</th><table class="table table-striped border-0 d-inline-block banque"><inputclass="form-check-input"type="checkbox"role="switch"id="annuel"bind:checked={annuel} />let annuel = truecrd[0] = detteremboursement[0] = 0interets[0] = 0crdAnnuel[0] = detteremboursementAnnuel[0] = 0interetsAnnuels[0] = 0for (let i = 1; i <= duree; i++) {if (i <= periodeRemboursementCapital) {remboursement[i] = 0} else if (i < nombreEcheancesPeriodeTronquee) {remboursement[i] = ppmt(i - periodeRemboursementCapital,totalPeriodes,-dette)} else if (i == nombreEcheances) {remboursement[i] = crd[i - 1]} else {remboursement[i] = 0}crd[i] = crd[i - 1] - remboursement[i]if (i % nombrePeriodesParAn == 0) {crdAnnuel[i / nombrePeriodesParAn] = crd[i]remboursementAnnuel[i / nombrePeriodesParAn] = 0interetsAnnuels[i / nombrePeriodesParAn] = 0}remboursementAnnuel[Math.floor(i / nombrePeriodesParAn)] +=remboursement[i]interetsAnnuels[Math.floor(i / nombrePeriodesParAn)] += interets[i]}}interets[i] = crd[i - 1] * taux_ * prorataPeriodiquetaux_ * prorataPeriodique,remboursement = new Array(duree + 1)crd = new Array(duree + 1)interets = new Array(duree + 1)remboursementAnnuel = new Array(dureeAns_)crdAnnuel = new Array(dureeAns_)interetsAnnuels = new Array(dureeAns_)// X11let periodeRemboursementCapital = dureeDifferee// W11let totalPeriodes = dureeAns * nombrePeriodesParAn - dureeDifferee// V9let nombreEcheances = Math.ceil(nombreEcheancesPeriodeTronquee)// V10let nombreEcheancesPeriodeTronquee = dureeAns * nombrePeriodesParAn$: {dureeAns = duree / nombrePeriodesParAndureeAns_ = Math.ceil(dureeAns)prorataPeriodique = 1 / nombrePeriodesParAnexport let remboursement = new Array(duree + 1)export let remboursementAnnuel = new Array(dureeAns_)export let crd = new Array(duree + 1)export let crdAnnuel = new Array(dureeAns_)export let interets = new Array(duree + 1)export let interetsAnnuels = new Array(dureeAns_)// V6let dureeAns = duree / nombrePeriodesParAnlet dureeAns_ = Math.ceil(dureeAns)let taux_ = Math.min(taux, 1)let prorataPeriodique = 1 / nombrePeriodesParAn// V8export let nombrePeriodesParAn = 12// Z7export let dureeDifferee = 1// Z8export let duree: number// V4export let taux: number// V3export let dette: numberimport { ppmt } from 'financial'
<script lang="ts"></script><div class="chart-container"><!-- Dots (if enabled) -->{#if showDots && !dotInfo}{#each I as i}<circlecx={xScale(xVals[i])}cy={yScale(yVals[i])}stroke={colors[colorVals[i]]}</g>{/each}{/if}<!-- Chart lines -->{#each lines as subsetLine, i}{#if dotInfo && false}{:else}{/if}</g>{/each}<!-- Y-axis and horizontal grid lines -->{#each yTicks as tick}<g class="tick" transform="translate(0, {yScale(tick)})">{#if horizontalGrid}{/if}<text x="-10" y="5">{tick + yFormat}</text></g>{/each}<text text-anchor="middle" x="0" y={marginTop - 10}>{yLabel}</text></g><!-- X-axis and vertical grid lines -->{#each xTicks as tick, i}<g class="tick" transform="translate({xScale(tick)}, 0)">{#if verticalGrid}{/if}</g>{/each}</g>{#each pointsScaled as point, i}<pathstroke="none"fill-opacity="0"class="voronoi-cell"d={voronoiGrid.renderCell(i)}{/each}</svg></div><!-- Tooltip -->{#if dotInfo}{subsets ? subsets[points[dotInfo[1]].color] : ''}:</div>{/if}<style></style>.tooltip {border-radius: 5px;padding: 5px;box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px,rgba(0, 0, 0, 0.3) 0px 7px 13px -3px,rgba(0, 0, 0, 0.2) 0px -3px 0px inset;opacity: 1;}@media (prefers-color-scheme: dark) {.y-axis text {fill: white;}.x-axis text {fill: white;}.tick-start {stroke: white;}.tick-grid {stroke: white;}.tick text {fill: white;}.domain {stroke: white;}}.y-axis .tick text {text-anchor: end !important;}.chart-container {justify-content: center;align-items: center;margin-top: 50px;margin-left: 8 0px;}svg {max-width: 100%;height: auto;height: 'intrinsic';margin: auto;}path {fill: 'green';}.y-axis {font-size: '10px';font-family: sans-serif;text-anchor: 'end';}.x-axis {font-size: '10px';font-family: sans-serif;text-anchor: 'end';}.tick text {font-size: 80%;fill: white !important;}.tick {opacity: 1;}.tick-start {stroke: black;stroke-opacity: 1;}.tick-grid {stroke: black;stroke-opacity: 0.2;font-size: '11px';color: black;}.tick text {fill: black;}{new Date(points[dotInfo[1]].x * 1000).toLocaleString()}: {points[dotInfo[1]].y.toFixed(2)}{yFormat}<divclass="tooltip"style="position:absolute; left:{dotInfo[2].pageX +12}px; top:{dotInfo[2].pageY +12}px; pointer-events:none; background-color:{tooltipBackground}; color:{tooltipTextColor}">on:mouseover={(e) => (dotInfo = [point, i, e])}on:focus={(e) => (dotInfo = [point, i, e])} /><text x={width - marginLeft - marginRight - 40} y={marginBottom}>{xLabel}</text><text font-size="8px" x={-marginLeft / 4} y="20">{xTicksFormatted[i] + xFormat}</text><lineclass="tick-grid"y2={-height + insetTop + marginTop} /><line class="tick-start" stroke="black" y2="6" /><gclass="x-axis"transform="translate(0,{height - marginBottom - insetBottom})"pointer-events="none"><pathclass="domain"stroke="black"d="M{marginLeft},0.5 H{width - marginRight}" /><lineclass="tick-grid"x1={insetLeft}x2={width - marginLeft - marginRight} /><lineclass="tick-start"x1={insetLeft - 6}x2={insetLeft} /><gclass="y-axis"transform="translate({marginLeft}, 0)"pointer-events="none"><pathclass="domain"stroke="black"d="M{insetLeft}, {marginTop} V{height - marginBottom + 6}" /><pathclass="line"fill="none"stroke={colors[i]}d={subsetLine}stroke-opacity={strokeOpacity[i]}stroke-width={strokeWidth}stroke-linecap={strokeLinecap}stroke-linejoin={strokeLinejoin} /><pathclass="line"fill="none"stroke-opacity={points[dotInfo[1]].color === i? '1': '0.1'}stroke={colors[i]}d={subsetLine}stroke-width={strokeWidth}stroke-linecap={strokeLinecap}stroke-linejoin={strokeLinejoin} /><circlecx={xScale(points[dotInfo[1]].x)}cy={yScale(points[dotInfo[1]].y)}{r}stroke={colors[points[dotInfo[1]].color]}fill={dotsFilled} /><g class="chartlines" pointer-events="none">fill={dotsFilled ? colors[colorVals[i]] : 'none'} />{r}<g class="dot" pointer-events="none"><svg{width}{height}viewBox="0 0 {width} {height}"cursor="crosshair"on:mouseout={() => (dotInfo = null)}on:blur={() => (dotInfo = null)}>const xTicks = xScale.ticks(xScalefactor)const xTicksFormatted = xTicks.map((el) =>new Date(el * 1000).toLocaleDateString())const yTicks = niceY.ticks(yScalefactor)const pointsScaled = points.map((el) => [xScale(el.x),yScale(el.y),el.color,])const delaunayGrid = Delaunay.from(pointsScaled)const voronoiGrid = delaunayGrid.voronoi([0, 0, width, height])$: {lines = []colors.forEach((_color, j) => {const filteredI = I.filter((_el, i) => colorVals[i] === j)lines.push(chartLine(filteredI))})}const chartLine = line().defined((i) => cleanData[i]).curve(curve).x((i) => xScale(xVals[i])).y((i) => yScale(yVals[i]))const xDomain = [xVals[0], xVals[xVals.length - 1]]const yDomain = [0, Math.max(...yVals)]const xScale = xType(xDomain, xRange)const yScale = yType(yDomain, yRange)const niceY = scaleLinear().domain([0, Math.max(...yVals)]).nice()const I = range(xVals.length)const gaps = (_d, i) => !isNaN(xVals[i]) && !isNaN(yVals[i])const cleanData = points.map(gaps)// For a single set of dataconsole.log(data)if (!('data' in data[0])) {x = Object.keys(data[0])[0]y = Object.keys(data[0])[1]xVals = data.map((el: Record<string, any>) => el[x])yVals = data.map((el: Record<string, any>) => el[y])colorVals = data.map((_) => 0)points = data.map((el) => ({x: el[x],y: el[y],color: 0,}))}// For data with subsets (NOTE: expects 'id' and 'data' keys)else {x = Object.keys(data[0]?.data[0])[0]y = Object.keys(data[0]?.data[0])[1]data.forEach((subset, i) => {subset.data.forEach((coordinate) => {xVals.push(coordinate[x])yVals.push(coordinate[y])colorVals.push(i)points.push({x: coordinate[x],y: coordinate[y],color: i,})})subsets.push(subset.id)})}let x: string,y: string,dotInfo,lines,xVals = [],yVals = [],points = [],subsets = [],colorVals = []const marginTop = 100 // the top margin, in pixelsconst marginRight = 0 // the right margin, in pixelsconst marginBottom = 30 // the bottom margin, in pixelsconst marginLeft = 100 // the left margin, in pixelsconst inset = 0 // inset the default range, in pixelsconst width = 600 // the outer width of the chart, in pixelsconst height = 400 // the outer height of the chart, in pixelsconst xLabel = '' // a label for the y-axisconst yLabel = 'Consommation (Wh)' // a label for the y-axisconst xFormat = '' // a format specifier string for the y-axisconst yFormat = 'Wh' // a format specifier string for the y-axisconst horizontalGrid = true // show horizontal grid linesconst verticalGrid = true // show vertical grid linesconst colors = ['#b81111', '#ffbf00'] // fill color for dots && number of colors in fill array MUST match number of subsets in dataconst showDots = false // whether dots should be displayedconst dotsFilled = true // whether dots should be filled or outlinedconst r = 0 // (fixed) radius of dots, in pixelsconst strokeWidth = 1 // stroke width of line, in pixelsconst strokeOpacity = [0.8, 0.2, 0.8, 0.8] // stroke opacity of lineconst tooltipBackground = 'white' // background color of tooltipconst tooltipTextColor = 'black' // text color of tooltipconst strokeLinecap = 'round' // stroke line cap of the lineconst strokeLinejoin = 'round' // stroke line join of the lineconst xScalefactor = width / 150 //y-axis number of valuesconst yScalefactor = height / 40 //y-axis number of valuesconst curve = curveLinear // method of interpolation between pointsconst xType = scaleUtc // type of x-scaleconst insetTop = 50 // inset from topconst insetRight = inset // inset from rightconst insetBottom = inset // inset fro bottomconst insetLeft = inset // inset from leftconst xRange = [marginLeft + insetLeft, width - marginRight - insetRight] // [left, right]const yType = scaleLinear // type of y-scaleconst yRange = [height - marginBottom - insetBottom, marginTop + insetTop] // [bottom, top]export let data//@ts-nocheckimport {line,curveLinear,Delaunay,range,scaleLinear,scaleUtc,} from 'd3'// import data from './line-data';
import { sample, chooseScale, relayout } from '../../../plot'export let position: { lat: number; lng: number } | undefinedexport let azimuth: number | undefinedexport let inclinaison: number | undefinedexport let puissance: numberexport let prmsFeature: boolean = falseexport let id: string = ''export let prms: Grain.Prm[] = []export let prodWeekly: number[] = []export let maxProd: number = 0export let assets = a as stringexport let server = 'https://coturnix.fr'
import { chooseScale, relayout, modeBarButtonsToRemove } from '../../../../plot'import type {Derived, PVGIS} from '../calcul'import * as calcul from '../calcul'export let data: {id: string,accesParticuliers: boolean,prms: Grain.Prm[],guest?: boolean,profils: Grain.Profil[],};console.log(data)export let assets: string = a;let s: Derived = getContext("simulation")let expert: Writable<boolean> = getContext("expert")let _communaute: Writable<(Grain.Communaute & {isMoving?: boolean}) | null> = getContext("communaute")let prmsFeature: boolean = !data.guestconst server = dev ? 'http://localhost:5173' : 'https://coturnix.fr'
let pvgis: null | {inputs: {location: { latitude: number; longitude: number }mounting_system: {fixed: { slope: { value: number }; azimuth: { value: number } }}}outputs: {hourly: {time: string'G(i)': numberP: numberT2m: numberH_sun: numberInt: number}[]}} = null
$: {initialised && updatePlot(puissance, position, azimuth, inclinaison)}
let puissance = 0let communaute: null | (Grain.Communaute & { isMoving?: boolean }) = null
await updatePlot(puissance, position, azimuth, inclinaison)
s.p.puissance.subscribe((value) => {puissance = valueinitialised && updatePlot(puissance, communaute)})_communaute.subscribe((value) => {communaute = valueinitialised && updatePlot(puissance, communaute)})
.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {darkMode = !!event.matchesredraw()})
.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {darkMode = !!event.matchesredraw()})
let profils: Record<string, { profils: string[] }> = {'Résidentiel ≤6kVA': {profils: ['RES1-P1'],},'Résidentiel 6>kVA≤36': {profils: ['RES11-P1'],},'Résidentiel ≤36kVA HPHC': {profils: ['RES2'],},'Professionel ≤36kVA': {profils: ['PRO1-P1'],},'Professionnel ≤36kVA HWE': {profils: ['PRO1WE'],},
'Entreprise 36<kVA<250 HPHC ETE-HIV': {profils: ['ENT1'],},/*"Production hydraulique":{"profils":[ "PRD1-P1" ]},"Production cogénération":{"profils":[ "PRD2-P1" ]},"Production photovoltaïque":{"profils":[ "PRD3-P1" ]},*/}
pr.splice(i, 1)pr = prawait updatePlot(puissance, position, azimuth, inclinaison)}async function profile(name: string): Promise<{ coefs: number[]; total: number }> {prm_loading = trueawait tick()let profiles = await Promise.all(profils[name].profils.map(async (x) => {let resp = await fetch(`${assets}/profiles/${x}.csv`)return await resp.text()}))prm_loading = falseawait tick()let annee = []let total = 0let file = 0for (let p of profiles) {let l = 0for (let line of p.split('\n')) {if (!line) {continue}const col = line.split(',')// const week = parseInt(col[0])// const dow = parseInt(col[1])const cs = parseFloat(col[2])const cj = parseFloat(col[3])for (let i = 0; i < 48; i++) {let ch = parseFloat(col[5 + i])const c = cs * cj * chif (file == 0) {annee.push(c)} else {annee[l] += c}total += c}l += 1}file += 1}return { coefs: annee, total }
data.profils.splice(i, 1)data.profils = data.profilsawait updatePlot(puissance, communaute)
let auto = 0let totalProd = 0let totalConso = 0let soleil = {hourly: {
let auto = 0let totalProd = 0let totalConso = 0let soleil = {hourly: {x: <string[]>[],y: <number[]>[],},daily: {x: <string[]>[],y: <number[]>[],},weekly: {x: <string[]>[],y: <number[]>[],},
},daily: {x: <string[]>[],y: <number[]>[],},weekly: {x: <string[]>[],y: <number[]>[],},x: <string[]>[],y: <number[]>[],type: 'bar',line: {color: '#ffbf00',width: 1,},marker: {color: '#ffbf00',width: 1,},name: 'Production solaire',bucket: 1,}
type: 'bar',line: {color: '#ffbf00',width: 1,},marker: {color: '#ffbf00',width: 1,},name: 'Production solaire',bucket: 1,}
let somme = {hourly: {x: <string[]>[],y: <number[]>[],},daily: {x: <string[]>[],y: <number[]>[],},weekly: {
let somme = {hourly: {x: <string[]>[],y: <number[]>[],},daily: {x: <string[]>[],y: <number[]>[],},weekly: {x: <string[]>[],y: <number[]>[],},
},x: <string[]>[],y: <number[]>[],type: 'bar',line: {color: '#b81111',width: 1,},marker: {color: '#b81111',width: 1,},name: 'Consommation totale',bucket: 1,}
type: 'bar',line: {color: '#b81111',width: 1,},marker: {color: '#b81111',width: 1,},name: 'Consommation totale',bucket: 1,}
async function updatePv(puissance: number,position?: { lat: number; lng: number },azimuth?: number,inclinaison?: number) {let d = new Date()let min_t = new Date(d.getFullYear(), 0, 1).getTime() / 1000let max_t = min_t + 364 * 24 * 3600if (!pvgis ||(position !== undefined &&pvgis.inputs.location.latitude != position?.lat) ||(position !== undefined &&pvgis.inputs.location.longitude != position?.lng) ||pvgis.inputs.mounting_system.fixed.slope.value != inclinaison ||pvgis.inputs.mounting_system.fixed.azimuth.value != azimuth
async function updatePv(puissance: number,communaute: (Grain.Communaute&{isMoving?: boolean}) | null,
await tick()let annuelle = await fetch(`${base}/pvgis`, {method: 'POST',body: JSON.stringify({lat: position?.lat,lng: position?.lng,azimuth: azimuth || 0,inclinaison: inclinaison || 45,}),})pvgis = await annuelle.json()
pvgis = await calcul.updatePv(puissance, communaute, pvgis, soleil, base)
}soleil.hourly = { x: [], y: [] }soleil.daily = { x: [], y: [] }soleil.weekly = { x: [], y: [] }maxProd = 0const start = new Date(min_t * 1000)const janvier = new Date(start.getFullYear(), 0, 1)for (let t = min_t; t < max_t; t += 3600) {let tt = new Date(t * 1000)let tts = tt.toLocaleString()let tds = tt.toLocaleDateString()let pp = 0if (pvgis) {let len = pvgis.outputs.hourly.lengthlet n = ((t * 1000 - janvier.getTime()) / 3600000) % lenconst pvvar = 'P'pp =(pvgis.outputs.hourly[Math.floor(n)][pvvar] * puissance) /1000soleil.hourly.x.push(tts)soleil.hourly.y.push(pp)if ((t - min_t) % (24 * 3600) == 0) {if ((t - min_t) % (24 * 7 * 3600) == 0) {if (soleil.weekly.y.length) {maxProd = Math.max(maxProd,soleil.weekly.y[soleil.weekly.y.length - 1])}soleil.weekly.x.push(tds)soleil.weekly.y.push(pp)} else {soleil.weekly.y[soleil.weekly.y.length - 1] += pp}soleil.daily.x.push(tds)soleil.daily.y.push(pp)} else {soleil.daily.y[soleil.daily.y.length - 1] += ppsoleil.weekly.y[soleil.weekly.y.length - 1] += pp}}
async function updatePlot(puissance: number,position?: { lat: number; lng: number },azimuth?: number,inclinaison?: number) {if (!browser) {return}
await updatePv(puissance, position, azimuth, inclinaison)let annee_: Record<string, { t: number[]; y: number[]; i: number }> = {}let d = new Date()const min_t = new Date(d.getFullYear(), 0, 1).getTime() / 1000const max_t = min_t + 364 * 24 * 3600for (let [prm, r] of Object.entries(annee)) {let d: { t: number[]; y: number[]; i: number } = {t: [],y: [],i: 0,
async function updatePlot(puissance: number,communaute: (Grain.Communaute & { isMoving?: boolean }) | null,) {console.log("isMoving", communaute?.isMoving)if(communaute?.isMoving) {return
let i = 0for (let t = min_t; t < max_t; t += 1800) {d.t.push(t)d.y.push(r[i] / 1000)i += 1}annee_[prm] = d}}let auto_ = sample(min_t, max_t, annee_, somme, soleil)auto = auto_.autototalProd = auto_.totalProdtotalConso = auto_.totalConsoif (!Plotly)//@ts-ignorePlotly = await import('plotly.js-dist')if (pr.length > 0) {let pi = 0;for (let p of pr) {let d: { t: number[]; y: number[]; i: number } = {t: [],y: [],i: 0,}let i = 0for (let t = min_t; t <= max_t; t += 1800) {d.t.push(t)let pp = profs.get(p.type)if (!pp) {profs.set(p.type, await profile(p.type))}if (pp) {d.y.push((pp.coefs[i] * p.conso) / pp.total)}i += 1}annee_[pi] = dpi += 1
if (!browser) {return
await tick()redraw()}let has_plot = { has_plot: false }let layout = {}
await updatePv(puissance, communaute)console.log("updated")prm_loading = true;let auto_ = await calcul.updatePlot(assets,annee,data.profils,profils_,somme,soleil,)prm_loading = false;await tick()auto = auto_.autototalProd = auto_.totalProdtotalConso = auto_.totalConsoconsole.log("updated auto", auto, totalProd, totalConso)if (!Plotly)//@ts-ignorePlotly = await import('plotly.js-dist')
function relayout_(event: any) {console.log('relayout_')const plot = document.getElementById('plot')!relayout(event, plot, Plotly, layout, has_plot, soleil, somme)}function redraw() {const plot = document.getElementById('plot')if (!plot) returnchooseScale(soleil)chooseScale(somme)let unite: stringif (soleil.bucket == 24) {unite = 'kWh/jour'} else if (soleil.bucket == 24 * 7) {unite = 'kWh/semaine'} else {unite = 'kWh'
await tick()redraw()
layout = {title: 'Autoconsommation',autosize: true,automargin: true,plot_bgcolor: '#fff0',paper_bgcolor: '#fff0',font: {color: darkMode ? '#fff' : '#000',},xaxis: {gridcolor: darkMode ? '#444' : '#bbb',ticklabeloverflow: 'allow',ticklabelstep: 4,tickangle: 45,},yaxis: {title: { text: unite },gridcolor: darkMode ? '#444' : '#bbb',color: darkMode ? '#fff' : '#000',},showlegend: true,legend: {xanchor: 'center',x: 0.5,y: -0.4,orientation: 'h',},}plotStyle = 'min-height:450px'if (has_plot.has_plot) {Plotly!.update(plot, [soleil, somme], layout)} else {Plotly!.newPlot(plot, [soleil, somme], layout, {responsive: true,})//@ts-ignoreplot.on('plotly_relayout', relayout_)has_plot.has_plot = true}
var dataProd = [{values: [Math.round(auto), Math.round(totalProd - auto)],labels: ['Autoconsommé', 'Surplus'],marker: { colors: ['#ffbf00dd', '#b81111dd'] },type: 'pie',sort: false,hole: 0.5,},]
let has_plot = { has_plot: false };let layout = {};
var dataConso = [{values: [Math.round(auto), Math.round(totalConso - auto)],labels: ['Autoproduit', 'Fournisseur'],marker: { colors: ['#ffbf00dd', '#b81111dd'] },type: 'pie',sort: false,hole: 0.5,},]
function relayout_(event: any) {console.log('relayout_')const plot = document.getElementById('plot')!relayout(event, plot, Plotly, layout, has_plot, soleil, somme)}
plotStyle = 'min-height:450px'Plotly.react('autoProd',dataProd,{title: "Autoconsommation sur l'année",
function redraw() {console.log("redraw")const plot = document.getElementById('plot')if (!plot) returnchooseScale(soleil)chooseScale(somme)let unite: stringif (soleil.bucket == 24) {unite = 'kWh/jour'} else if (soleil.bucket == 24 * 7) {unite = 'kWh/semaine'} else {unite = 'kWh'}layout = {title: 'Autoconsommation',autosize: true,automargin: true,plot_bgcolor: '#fff0',paper_bgcolor: '#fff0',
},{ responsive: true })Plotly.react('autoConso',dataConso,{title: "Autoproduction sur l'année",font: {
yaxis: {title: { text: unite },gridcolor: darkMode ? '#444' : '#bbb',
},}plotStyle = 'min-height:450px'if (has_plot.has_plot) {Plotly!.update(plot, [soleil, somme], layout)} else {Plotly!.newPlot(plot, [soleil, somme], layout, {responsive: true,modeBarButtonsToRemove: modeBarButtonsToRemove,})//@ts-ignoreplot.on('plotly_relayout', relayout_)has_plot.has_plot = true}var dataProd = [{values: [Math.round(auto), Math.round(totalProd - auto)],labels: ['Autoconsommé', 'Surplus'],marker: { colors: ['#ffbf00dd', '#b81111dd'] },type: 'pie',sort: false,hole: 0.5,},]var dataConso = [{values: [Math.round(auto), Math.round(totalConso - auto)],labels: ['Autoproduit', 'Fournisseur'],marker: { colors: ['#ffbf00dd', '#b81111dd'] },type: 'pie',sort: false,hole: 0.5,},]plotStyle = 'min-height:450px'Plotly.react('autoProd',dataProd,{title: "Autoconsommation sur l'année",font: {color: darkMode ? '#fff' : '#000',},plot_bgcolor: '#fff0',paper_bgcolor: '#fff0',autosize: true,legend: {orientation: 'h',},
},{ responsive: true })}
{ responsive: true })Plotly.react('autoConso',dataConso,{title: "Autoproduction sur l'année",font: {color: darkMode ? '#fff' : '#000',},plot_bgcolor: '#fff0',paper_bgcolor: '#fff0',autosize: true,legend: {orientation: 'h',},},{ responsive: true })}
id="erreur"class="alert alert-danger fade d-flex"class:show={erreur}role="alert">{erreur}<buttontype="button"class="btn-close"style="padding:20px;margin:-16px -16px -16px auto"on:click={() => (erreur = '')}aria-label="Close" /></div>{/if}<h2 class="my-5">Profils de consommation</h2>{#if pr.length}<table class="table"><thead><tr><th /><th>Profil</th><th>Consommation annuelle (kWh)</th><th /></tr></thead><tbody>{#each pr as pr, i}<tr><td class="px-3 align-middle">Profil</td><td><selectclass="form-select"bind:value={pr.type}on:change={(_ev) =>updatePlot(puissance,position,azimuth,inclinaison)}>{#each Object.entries(profils) as [p, _]}<option value={p}>{p}</option>{/each}</select></td><td><inputtype="number"class="form-control"placeholder="Abonnement (kW)"min="0"step="500"bind:value={pr.conso}on:change={(_ev) =>updatePlot(puissance,position,azimuth,inclinaison)} /><!-- <inputtype="number"class="form-control"placeholder="Nombre de compteurs"min="0"step="1"bind:value={pr.nCompteurs}on:change={(_ev) => updatePlot(puissance, position, azimuth, inclinaison)} /> --></td><td style="width:1%;white-space:nowrap;text-align:center"><buttonclass="btn btn-link"on:click={(_ev) => delProfil(i)}><i class="bi bi-x-circle text-primary" /></button></td></tr>{/each}</tbody></table>{/if}<div class="text-center"><button class="btn btn-primary" on:click={addProfil}><i class="bi bi-plus-circle" /> Ajouter un profil</button></div>
class="tab-pane fade active show p-3"id="simu-tab-pane"role="tabpanel"aria-labelledby="simu-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold">Production… <span class="text-primary">Consommation</span></h3>
{#if prmsFeature}<h2 class="my-5">Relevés de compteurs</h2>{#if prms.length}<table class="table"><thead><tr><th>PRM<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Un PRM, aussi appelé PDL, est l'identifiant d'un compteur électrique. On peut le trouver sur les factures d'électricité, ou en appuyant sur le bouton + du compteur Linky."><i class="bi bi-question-circle" /></button></th><th>Prénom</th><th>Nom</th><th /></tr></thead><tbody>{#each prms as prm, i}<tr><td><inputclass="form-control"
<form method="POST" action="/api/grain" id="pdl" on:submit={refresh} />{#if erreur}<divid="erreur"class="alert alert-danger fade d-flex"class:show={erreur}role="alert">{erreur}<buttontype="button"class="btn-close"style="padding:20px;margin:-16px -16px -16px auto"on:click={() => (erreur = '')}aria-label="Close" /></div>{/if}<h2 class="my-5">Profils de consommation</h2>{#if data.profils.length}{#if $expert}<div>Vous êtes en mode expert. Dans ce mode, vous avez un accès direct à tous les profils utilisés par les opérateurs du réseau pour simuler les consommations. Voir <a href="https://www.enedis.fr/responsable-dequilibre-profilage-et-profils">la documentation</a>.</div>{/if}<table class="table"><thead><tr><th /><th>Profil</th><th>Consommation annuelle (kWh)</th><th /></tr></thead><tbody>{#each data.profils as pr, i}<tr><td class="px-3 align-middle">Profil</td><td><selectclass="form-select"bind:value={pr.type}on:change={(_ev) =>updatePlot(puissance,communaute,)}>{#each Object.entries(profils_) as [p, _]}<option value={p}>{p}</option>{/each}</select></td><td><inputtype="number"class="form-control"placeholder="Abonnement (kW)"min="0"step="500"bind:value={pr.conso}on:change={(_ev) =>updatePlot(puissance,communaute,)} /></td><td style="width:1%;white-space:nowrap;text-align:center"><buttonclass="btn btn-link"on:click={(_ev) => delProfil(i)}><i class="bi bi-x-circle text-primary" /></button></td></tr>{/each}</tbody></table>{/if}<div class="text-center"><button class="btn btn-primary" on:click={addProfil}><i class="bi bi-plus-circle" /> Ajouter un profil</button></div>{#if prmsFeature}<h2 class="my-5">Relevés de compteurs</h2><div class="mb-5">Vous pouvez ajouter des compteurs d'entreprises ou d'associations par leur numéro de compteur et leur numéro de SIRET.</div>{#if data.prms.length}{#each data.prms as prm, i}<div class="row my-2"><div class="col-3 d-flex align-middle"><label class="ms-auto form-label" for="prm-{i}">PRM<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Un PRM, aussi appelé PDL, est l'identifiant d'un compteur électrique. On peut le trouver sur les factures d'électricité, ou en appuyant sur le bouton + du compteur Linky."><i class="bi bi-question-circle" /></button></label></div><div class="col d-flex align-items-center"><inputclass="form-control form-control-sm"id="prm-{i}"
type="number" /></td><td><inputclass="form-control"bind:value={prm.nom} /></td><td><inputclass="form-control"bind:value={prm.prenom} /></td><tdstyle="width:1%;white-space:nowrap;text-align:center">
on:keydown={(e) => (e.key == 'ArrowUp' || e.key == 'ArrowDown') && e.preventDefault() }type="number" /></div><div class="col col-auto d-flex align-items-center">
class="btn btn-link"on:click={(ev) => delPrm(ev, i)}><iclass="bi bi-x-circle text-primary" /></button></td></tr>
class="btn btn-outline-primary btn-sm"on:click={(e) => { e.preventDefault(); data.prms.splice(i, 1); data.prms = data.prms; }}><i class="bi bi-trash"/></button></div></div>{#if data.accesParticuliers}<div class="row my-2"><div class="col-9 ms-auto"><input type="radio" class="btn-check" id="particulier" value={false} bind:group={prm.pro}><label class="btn btn-sm" for="particulier">Particulier</label><input type="radio" class="btn-check" id="pro" value={true} bind:group={prm.pro}><label class="btn btn-sm" for="pro">Professionnel</label></div></div>{/if}{#if (!data.accesParticuliers) || prm.pro}<div class="row my-2"><div class="col-3 d-flex text-end align-items-center"><label class="ms-auto form-label" for="siret-{i}">SIRET du titulaire du compteur</label></div><div class="col-9 d-flex align-items-center"><inputclass="form-control form-control-sm"id="siret-{i}"bind:value={prm.siret} /></div></div>{:else}<div class="row my-2"><div class="col-3 d-flex text-end align-items-center"><label class="ms-auto form-label" for="prenom-{i}">Prénom du titulaire du compteur</label></div><div class="col-9 d-flex align-items-center"><inputclass="form-control form-control-sm"id="prenom-{i}"bind:value={prm.prenom} /></div></div><div class="row my-2"><div class="col-3 d-flex text-end align-items-center"><label class="ms-auto form-label" for="nom-{i}">Nom du titulaire du compteur</label></div><div class="col-9 d-flex align-items-center"><inputclass="form-control form-control-sm"id="nom-{i}"bind:value={prm.nom} /></div></div><div class="row my-2"><div class="col-3 d-flex text-end align-items-center"><label class="ms-auto form-label" for="numerorue-{i}">Numéro et rue</label></div><div class="col-9 d-flex align-items-center"><inputclass="form-control form-control-sm"id="numerorue-{i}"bind:value={prm.numerorue} /></div></div><div class="row my-2"><div class="col-3 d-flex text-end align-items-center"><label class="ms-auto form-label" for="codepostal-{i}">Code INSEE de la commune</label></div><div class="col-9 d-flex align-items-center"><inputclass="form-control form-control-sm"id="codepostal-{i}"bind:value={prm.codeinsee} /></div></div><div class="row my-2"><div class="col-3 d-flex text-end align-items-center"><label class="ms-auto form-label" for="serie-{i}">Numéro de série</label></div><div class="col-9 d-flex align-items-center"><inputclass="form-control form-control-sm"id="serie-{i}"bind:value={prm.serie} /></div></div>{/if}<hr class="my-4" />
<div class="my-3 form-check"><inputclass="form-check-input"type="checkbox"id="consentement"bind:checked={consentement} /><label class="form-check-label" for="consentement">Ces abonnés consentent à ce que j'accède à leurs données</label></div>{/if}<div class="d-flex justify-content-center"><button class="mx-2 btn btn-primary" on:click={addPrm}><i class="bi bi-plus-circle" /> Ajouter un compteur</button>{#if data.prms.length}<buttondisabled={!consentement}class="mx-2 btn btn-primary"form="pdl">Mettre à jour</button><divclass="mx-2 spinner-border text-primary"class:d-none={!prm_loading} />{/if}</div>{/if}<h2 class="mt-5">Simulation</h2><table class="table table-bordered d-inline-block"><tbody><tr><td>Production totale:</td><td>{Math.round(totalProd)}kWh</td></tr><tr><td>Consommation totale:</td><td>{Math.round(totalConso)}kWh</td></tr>
<div class="my-3 form-check"><inputclass="form-check-input"type="checkbox"id="consentement"bind:checked={consentement} /><label class="form-check-label" for="consentement">Ces abonnés consentent à ce que j'accède à leurs données</label>
{#if pvgis_loading}<div class="my-3 d-flex align-items-center text-primary"><div class="spinner-border text-primary me-3" />Chargement de la production solaire</div>{/if}<div class="my-3"><div class="mx-auto" style={plotStyle} id="plot" /></div><div class="my-3 row" class:d-none={totalProd == 0 || totalConso == 0}><div class="col-12 col-sm-6 p-0" style={plotStyle} id="autoProd" /><div class="col-12 col-sm-6 p-0" style={plotStyle} id="autoConso" />
{/if}<div class="d-flex justify-content-center"><button class="mx-2 btn btn-primary" on:click={addPrm}><i class="bi bi-plus-circle" /> Ajouter un compteur</button>{#if prms.length}<buttondisabled={!consentement}class="mx-2 btn btn-primary"form="pdl">Mettre à jour</button><divclass="mx-2 spinner-border text-primary"class:d-none={!prm_loading} />{/if}
{/if}<h2 class="mt-5">Simulation</h2><table class="table table-bordered d-inline-block"><tbody><tr><td>Production totale:</td><td>{Math.round(totalProd)}kWh</td></tr><tr><td>Consommation totale:</td><td>{Math.round(totalConso)}kWh</td></tr></tbody></table>{#if pvgis_loading}<div class="my-3 d-flex align-items-center text-primary"><div class="spinner-border text-primary me-3" />Chargement de la production solaire</div>{/if}<div class="my-3"><div class="mx-auto" style={plotStyle} id="plot" /></div>
<div class="my-3 row" class:d-none={totalProd == 0 || totalConso == 0}><div class="col-12 col-sm-6 p-0" style={plotStyle} id="autoProd" /><div class="col-12 col-sm-6 p-0" style={plotStyle} id="autoConso" />
<style>input::-webkit-outer-spin-button,input::-webkit-inner-spin-button {-webkit-appearance: none;margin: 0;}input[type=number] {appearance: none;-moz-appearance:textfield; /* Firefox */}</style>
<script lang="ts">import { getContext } from 'svelte'import type { Derived } from '../calcul'const d: Derived = getContext("simulation")const fmt2 = Intl.NumberFormat('fr-FR', { maximumFractionDigits: 2 })const fmt0 = Intl.NumberFormat('fr-FR', { maximumFractionDigits: 0 })function sum(x: number[], n: number) {let sum = 0;for(let i = 0; i < n; i++) {sum += x[i]}return sum}function format(x: number) {let f = 0let s = ''if (Math.abs(x) <= 1000) {s = ' €'f = x} else if (Math.abs(x) < 1000000) {s = ' k€'f = x / 1000} else if (Math.abs(x) < 1000000000) {s = ' M€'f = x / 1000000} else {s = ' mds€'f = x / 1000000000}return fmt2.format(f) + s}const N = d.Nconst resultatNet = d.resultatNetconst chiffreAffaires = d.chiffreAffairesconst arr = 0const autoprod = d.p.autoprodconst autoconso = d.p.autoconsoconst productionAnnuelle = d.productionAnnuelleconst tarifAllo = d.tarifAlloconst tarifLocal = d.tarifLocalconst tarifSurplus = d.tarifSurplusconst primeInvestissement = d.primeInvestissementconst charges = d.chargesconst ebe = d.ebeconst valeurAjoutee = d.valeurAjouteeconst accise = d.acciseconst taxeFonciere = d.taxeFonciereconst ifer = d.iferconst cfe = d.cfeconst provisionOnduleurs = d.provisionOnduleursconst interetsDetteSenior = d.interetsDetteSeniorconst interetsDSRA = d.interetsDSRAconst resultatFiscal = d.resultatFiscalconst is = d.isconst investissement = d.investissementconst amortissement = d.amortissementconst apport = d.p.apportconst dscr = d.dscrconst exploitation = d.exploitationconst turpe = d.turpeconst assurance = d.assuranceconst divers = d.diverslet an = new Date().getFullYear()</script><divclass="tab-pane fade active show p-3"id="compte-tab-pane"role="tabpanel"aria-labelledby="compte-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold"><span class="text-primary">Projetez-vous</span> dans votreprojet.</h3>Durée d'analyse : {$N} ans<h4 class="my-3 mt-5">Résultat</h4><div class="ms-3 row"><h5 class="mt-5">Résultat net</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum($resultatNet, $N))}</span></div><div class="d-flex flex-wrap"><div class="d-flex flex-column my-3"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Résultat net</div></div>{#each Array(Math.ceil($N)) as _, i}<div class="d-flex flex-column my-3"><div class="tcellt">{an + i}</div><div class="tcellt">{$resultatNet[i].toFixed(arr)}</div></div>{/each}</div></div><h4 class="my-3">Détail</h4><div class="ms-3"><h5 class="mt-5">Chiffre d'affaires</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum($chiffreAffaires, $N))}</span></div><div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Autoproduction (€)</div><div class="titreligne tcellt">Vente locale (€)</div><div class="titreligne tcellt">Vente de surplus (€)</div><div class="titreligne tcellt">Prime</div><div class="titreligne tcellt">Chiffre d'affaires</div></div>{#each Array(Math.ceil($N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{($productionAnnuelle[i] *(($autoprod * $tarifAllo[i]) / 100)).toFixed(0)}</div><div class="tcellt">{($productionAnnuelle[i] *(($autoconso * $tarifLocal[i]) / 100)).toFixed(0)}</div><div class="tcellt">{($productionAnnuelle[i] *((1 - $autoconso/100) * $tarifSurplus[i])).toFixed(0)}</div><div class="tcellt">{#if i == 0}{$primeInvestissement}{:else} {/if}</div><div class="tcellt">{$chiffreAffaires[i].toFixed(arr)}</div></div>{/each}</div><h5 class="my-3">Charges d'exploitation</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum($charges, $N))}</span></div><div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Maintenance (€)</div><div class="titreligne tcellt">TURPE (€)</div><div class="titreligne tcellt">Assurance (€)</div><div class="titreligne tcellt">Divers</div><div class="titreligne tcellt">Total charges</div></div>{#each Array(Math.ceil($N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{$exploitation[i].toFixed(arr)}</div><div class="tcellt">{$turpe[i].toFixed(arr)}</div><div class="tcellt">{$assurance[i].toFixed(arr)}</div><div class="tcellt">{$divers[i].toFixed(arr)}</div><div class="tcellt">{$charges[i].toFixed(arr)}</div></div>{/each}</div><h5 class="my-3">Excédent brut d'exploitation</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum($ebe, $N))}</span></div><div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Valeur ajoutée (€)</div><div class="titreligne tcellt">Accise (€)</div><div class="titreligne tcellt">IFER (€)</div><div class="titreligne tcellt">Taxe foncière</div><div class="titreligne tcellt">Cotisation foncière des entreprises</div><div class="titreligne tcellt">Excédent brut d'exploitation</div></div>{#each Array(Math.ceil($N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{$valeurAjoutee[i].toFixed(arr)}</div><div class="tcellt">{$accise[i].toFixed(arr)}</div><div class="tcellt">{$ifer[i].toFixed(arr)}</div><div class="tcellt">{$taxeFonciere.toFixed(arr)}</div><div class="tcellt">{$cfe.toFixed(arr)}</div><div class="tcellt">{$ebe[i].toFixed(arr)}</div></div>{/each}</div><h5 class="my-3">Résultat net</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum($resultatNet, $N))}</span></div><div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Onduleurs<buttontype="button"class="btn btn-sm btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Provision pour le remplacement des onduleurs"><i class="bi bi-question-circle" /></button></div><div class="titreligne tcellt">Amortissement</div><div class="titreligne tcellt">Intérêts dette senior</div><div class="titreligne tcellt">Intérêts DSRA <buttontype="button"class="btn btn-sm btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Dans un emprunt, compte bancaire qui permettra de payer le service de la dette si jamais l'entité emprunteuse n'est plus en mesure de le faire."><i class="bi bi-question-circle" /></button></div><div class="titreligne tcellt">Résultat fiscal</div><div class="titreligne tcellt">Impôt sur les sociétés</div><div class="titreligne tcellt">Résultat net</div></div>{#each Array(Math.ceil($N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{$provisionOnduleurs[i].toFixed(arr)}</div><div class="tcellt">{$amortissement[i].toFixed(arr)}</div><div class="tcellt">{$interetsDetteSenior[i].toFixed(arr)}</div><div class="tcellt">{$interetsDSRA[i].toFixed(arr)}</div><div class="tcellt">{$resultatFiscal[i].toFixed(arr)}</div><div class="tcellt">{$is[i].toFixed(arr)}</div><div class="tcellt">{$resultatNet[i].toFixed(arr)}</div></div>{/each}</div>{#if $investissement > $apport}<h5 class="my-3">Couverture de la dette</h5><div class="d-flex flex-wrap"><div class="d-flex flex-column my-3"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">DSCR</div></div>{#each Array(Math.ceil($N)) as _, i}<div class="d-flex flex-column my-3"><div class="tcellt">{an + i}</div><div class="tcellt">{fmt2.format($dscr[i])}</div></div>{/each}</div>{/if}</div></div><style lang="scss">h5 {font-size: 1.5rem;font-weight: bold;}h4 {font-size: 2rem;font-weight: bold;}.tcellt {border-bottom: 1px solid $primary;}.tcellt:last-child {border-bottom: unset;}.tcellt {padding: 8px 10px;}</style>
<script lang="ts">import { Derived } from '../calcul'import { getContext } from 'svelte'const _simulation: Derived = getContext("simulation")const inflationElec = _simulation.p.inflationElecconst enedis = _simulation.p.enedisconst productible = _simulation.p.productibleconst ratio = _simulation.p.ratioconst degradation = _simulation.p.degradationconst tauxDSRA = _simulation.p.tauxDSRAconst inflation = _simulation.p.inflation</script><divclass="tab-pane fade show active p-3"id="expert-tab-pane"role="tabpanel"aria-labelledby="expert-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold">D'avantage de <span class="text-primary">réglages</span></h3><div class="mt-5"><div class="mb-3"><label class="form-label" for="enedis">Coût de raccordement Enedis</label><inputclass="form-control form-control-sm w-auto"type="number"id="enedis"min="0"bind:value={$enedis} /></div><div class="mb-3"><label class="form-label" for="productible">Productible annuel (kWh/kWc)</label><inputclass="form-control form-control-sm w-auto"type="number"id="productible"min="0"bind:value={$productible} /></div><div class="mb-3"><label class="form-label" for="ratio">Ratio installation (€/W)</label><inputclass="form-control form-control-sm w-auto"type="number"id="ratio"min="0"bind:value={$ratio} /></div><div class="mb-3"><label class="form-label" for="degradation">Taux de dégradation PV(%)</label><inputclass="form-control form-control-sm w-auto"type="number"id="degradation"min="0"bind:value={$degradation} /></div><div class="mb-3"><label class="form-label" for="dsra">Taux d'intérêt DSRA (%)</label><inputclass="form-control form-control-sm w-auto"type="number"id="dsra"min="0"step="any"bind:value={$tauxDSRA} /></div><div class="mb-3"><label class="form-label" for="inflation">Inflation tarif local</label><inputclass="form-control form-control-sm w-auto"type="number"id="inflation"min="0"bind:value={$inflation} /></div><div class="mb-3"><label class="form-label" for="inflationElec">Inflation nationale de l'électricité (%)</label><inputclass="form-control form-control-sm w-auto"type="number"bind:value={$inflationElec}min="0"id="inflationElec" /></div><!-- <div class="my-3"><label class="form-label" for="surfacekwc">Surface d'un kWc (m²)</label><inputclass="form-control form-control-sm"type="number"id="surfacekwc"step ="any"value={surface_kwc}on:change={onRatioChange} /></div>--></div></div>
export let position: undefined | { lat: number; lng: number }export let azimuth: undefined | numberexport let inclinaison: undefined | number
import type { Grain } from '../../../../grain-types.ts';import type { Writable } from 'svelte/store'import { getContext } from 'svelte'
type Compteur_ = Grain.Compteur & { dot?: any };type Communaute_ = Grain.Communaute & { compteurs?: Compteur_[], circle?: any, isMoving?: boolean };let communaute: Writable<Communaute_ | null> = getContext("communaute")export let save: () => Promise<void> = async () => { };let derogation = $communaute?.derogation ?? "non"
let circleOpts = {radius: 1000,stroke: true,fillOpacity: 0,color: '#b81413',weight: 3,}let leaflet: any = null;function changeDerogation (e: any) {console.log('Changederogation', derogation, $communaute, e)if($communaute) {$communaute.derogation = derogation;let d = derogationRayonKm() * 1000checkRayon($communaute, d)if ($communaute?.circle) {$communaute.circle.setRadius(d)} else {circleOpts.radius = d$communaute.circle = leaflet.circle($communaute, circleOpts)$communaute.circle.addTo(map!)}save()}}function derogationRayonKm(): number {if ($communaute?.derogation == "20") {return 10} else if ($communaute?.derogation == "10") {return 5} else {return 1}}type Commune = {nom: String,densite: number,}let communes: null | Commune[] = nullasync function checkRayon(latlng: Grain.LatLng| null, rayon: number) {if(latlng) {console.log("checkRayon");let resp = await fetch(`${srv}/api/intersect?lat=${latlng.lat}&lng=${latlng.lng}&rayon=${rayon}`, {credentials: 'include',})if (resp.status == 200) {communes = await resp.json()}}}
iconUrl: `${assets}/marker-icon-red.png`,iconRetinaUrl: `${assets}/marker-icon-red.png`,shadowUrl: `${assets}/marker-shadow.png`,
iconUrl: `${assets}/signal-red.png`,iconRetinaUrl: `${assets}/signal-red.png`,
})let initialCentre: null | { lat: number; lng: number } = nulllet invalidDrag = falsemap.on('mousemove', function (e: any) {if (invalidDrag) {return}if ($communaute?.circle && e.originalEvent.shiftKey) {let m = document.getElementById('descrmap')m!.focus()let d = derogationRayonKm() * 1000if (initialCentre) {let testCentre = {lat: e.latlng.lat - initialCentre.lat,lng: e.latlng.lng - initialCentre.lng,}if (!checkDistance(testCentre, $communaute.compteurs ?? [], d)) {invalidDrag = truereturn}$communaute.lat = testCentre.lat$communaute.lng = testCentre.lng$communaute.circle.setLatLng($communaute);} else {if (map.distance($communaute, e.latlng) <= d) {$communaute.isMoving = true;initialCentre = {lat: e.latlng.lat - $communaute.lat,lng: e.latlng.lng - $communaute.lng,}}}}
position = e.latlngconsole.log(position)if (dot) {dot.setLatLng(position!)
console.log(e);if(e.originalEvent.shiftKey) {// Déplacer/ajouter la communautéif($communaute?.circle) {if(checkDistance(e.latlng, $communaute.compteurs ?? [], derogationRayonKm() * 1000)) {$communaute.circle.setLatLng(e.latlng)$communaute.lat = e.latlng.lat$communaute.lng = e.latlng.lng}} else {if(!$communaute) {$communaute = { lat: e.latlng.lat, lng: e.latlng.lng, derogation: derogation }}circleOpts.radius = derogationRayonKm() * 1000;$communaute!.circle = L.circle(e.latlng,circleOpts)$communaute!.circle.addTo(map)}checkRayon($communaute!, derogationRayonKm() * 1000)save()
dot = L.marker(position!, {icon: redIcon!,draggable: false,})dot.addTo(map)
if(!$communaute) {communaute.set({lat: e.latlng.lat,lng: e.latlng.lng,compteurs: [],derogation,})circleOpts.radius = derogationRayonKm() * 1000;$communaute!.circle = L.circle(e.latlng,circleOpts).addTo(map)checkRayon($communaute, derogationRayonKm() * 1000)}if(map.distance($communaute!, e.latlng) <= derogationRayonKm() * 1000) {let dot = {lat: e.latlng.lat,lng: e.latlng.lng,dot: L.marker(e.latlng, {icon: redIcon!,draggable: false,}),azimuth: 0,inclinaison: 0,puissance: 0,};dot.dot.addTo(map)if(!$communaute!.compteurs) {$communaute!.compteurs = []}$communaute!.compteurs.push(dot);$communaute = $communaute;}save()
map.setView(position, zoom)
let lng0 = 180let lng1 = -180let lat0 = 90let lat1 = -90let mar = 0.5let dy = (mar + (derogationRayonKm())) / 111let lat2 = $communaute.lat - dylet lat3 = $communaute.lat + dylet dx = dy / Math.cos(($communaute.lat * Math.PI) / 180)let lng2 = $communaute.lng - dxlet lng3 = $communaute.lng + dxlng0 = Math.min(lng0, lng2)lng1 = Math.max(lng1, lng3)lat0 = Math.min(lat0, lat2)lat1 = Math.max(lat1, lat3)if (lng0 < lng1 && lat0 < lat1) {console.log([[lat0, lng0],[lat1, lng1],])map.fitBounds([[lat0, lng0],[lat1, lng1],],)} else {map.setView($communaute, zoom)}}function checkDistance(e: { lat: number; lng: number },compteurs: { lat: number, lng: number }[],d: number): boolean {for (let v of compteurs) {let dist = map.distance(e, v)if (dist > d) {return false}}return true}function deleteCompteur(event: any,i: number,) {event.preventDefault();$communaute!.compteurs![i].dot.removeFrom(map);$communaute!.compteurs!.splice(i, 1);$communaute = $communaute;save()
<div id="descrmap" class="w-100" style="height:500px" /><div class="d-flex flex-column"><buttonclass="btn btn-sm btn-outline-secondary my-2 ms-auto"on:click={(_) => recentrer(zoom)}>Recentrer</button>
<divclass="tab-pane fade active show p-3"id="description-tab-pane"role="tabpanel"aria-labelledby="description-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold">Quelques <span class="text-primary">détails</span> de plus.</h3>
<label class="form-label" for="azimuth">Azimuth (degrés par rapport au Nord):</label><inputtype="number"class="form-control"id="azimuth"bind:value={azimuth} />
Cliquez sur la carte ci-dessous pour ajouter des compteurs, ou cliquez en maintenant la touche Shift pour positionner la communauté.
<div class="my-3"><label class="form-label" for="incl">Inclinaison:</label><inputtype="number"class="form-control"id="incl"bind:value={inclinaison} />
<div id="descrmap" class="w-100" style="height:500px" /><div class="d-flex flex-column"><buttonclass="btn btn-sm btn-outline-secondary my-2 ms-auto"on:click={(_) => recentrer(zoom)}>Recentrer</button>{#if $communaute?.compteurs?.length}<div class="my-3"><table class="table"><thead><tr><th>Puissance</th><th>Azimuth (le Sud est à 0°, l'Est à -90°, l'Ouest à 90°)</th><th>Inclinaison</th><th></th></tr></thead><tbody>{#each $communaute.compteurs as c, i}<tr><td><inputtype="number"class="form-control"bind:value={c.puissance} /></td><td><inputtype="number"class="form-control"bind:value={c.azimuth} /></td><td><inputtype="number"class="form-control"bind:value={c.inclinaison} /></td><td><button class="btn btn-sm btn-outline-primary" on:click={(e) => deleteCompteur(e, i)}><i class="bi bi-trash"/></button></td></tr>{/each}</tbody></table></div>{/if}<div class="p-3 overflow-auto"><label for="derog" class="form-label">Extension de périmètre (habitat rural et/ou dispersé)</label> <button type="button"class="btn btn-link align-baseline p-0"id="ext-popover"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Par dérogation, le rayon de la communauté peut être étendu à 5km ou 10km."><i class="bi bi-question-circle" /></button><selectid="derog"class="form-select form-select-sm w-auto d-inline"name="derog"bind:value={derogation}on:change={changeDerogation}><option value="non">Non</option><option value="10">10km</option><option value="20">20km</option></select>{#if communes && communes.length}<h4 class="mt-5">Communes concernées</h4><table class="table"><thead><th>Nom</th><th>Éligibilité</th></thead><tbody>{#each communes as c}<tr><td>{c.nom}</td><td>{#if c.densite >= 5}20km{:else if c.densite >= 3}10km{#if derogation=='20' }<i class="ms-3 text-danger bi bi-exclamation-triangle"/>{/if}{:else}2km{#if derogation!='non' }<i class="ms-3 text-danger bi bi-exclamation-triangle"/>{/if}{/if}</td></tr>{/each}</tbody></table>{/if}</div>
import { readable, writable, derived } from 'svelte/store';import type { Readable, Writable } from 'svelte/store';import type { Grain } from '../../../helpers'import * as H from '../../../helpers'import type { Sample } from '../../../plot'import { sample } from '../../../plot'import { tick } from 'svelte'type _SimulationCommunaute = {[Property in keyof Grain.SimulationCommunaute]: Writable<Grain.SimulationCommunaute[Property]>;}export class Derived {p: _SimulationCommunaute;s: Grain.SimulationCommunauteconstructor(s: Grain.SimulationCommunaute) {this.s = sthis.p = <_SimulationCommunaute>Object.fromEntries(Object.entries(s).map(([k, v], _) => {let w = writable(v);w.subscribe((value) => this.s[k as keyof Grain.SimulationCommunaute] = value as any)return [k, w]}));this.N = writable(30);this.investissement = derived([this.p.puissance, this.p.ratio, this.p.enedis],([$puissance, $ratio, $enedis]) => $puissance ? $puissance * 1000 * $ratio + $enedis : 0)this.apport = derived([this.investissement, this.p.apport],([$investissement, $apport]) => Math.min($investissement, $apport))this.primeInvestissement = derived(this.p.puissance,($puissance) => ($puissance <= 3 ? 0.30: $puissance <= 9 ? 0.23: $puissance <= 36 ? 0.20: $puissance <= 100 ? 0.10: 0) * 1000 * $puissance)function tarifSurplus_(tarifCRE: any, puissance: number): number {let resultat = tarifCRE.prix[0]for (let i = 0; i < tarifCRE.limites.length; i++) {if (puissance < tarifCRE.limites[i]) {return resultat} else {resultat = tarifCRE.prix[i]}}return resultat}this.tarifSurplus = derived([this.p.tarifCRE, this.p.puissance, this.N],([$tarifCRE, $puissance, $N],_,update) => {update((tarifSurplus) => {if (!tarifSurplus || tarifSurplus.length < $N) {tarifSurplus = Array($N);}let t = tarifSurplus_($tarifCRE, $puissance);for (let i = 0; i < $N; i++) {tarifSurplus[i] = t * Math.pow(1 + $tarifCRE.inflation / 100, i)}return tarifSurplus})});this.productionAnnuelle = derived([this.p.productible, this.p.puissance, this.p.degradation, this.N],([$productible, $puissance, $degradation, $N], _, update) => {update((prod) => {if (!prod || prod.length < $N) {prod = Array($N);}for (let i = 0; i < $N; i++) {prod[i] = H.productionAnnuelle($productible, $puissance, $degradation, i)}return prod})})this.tarifLocal = derived([this.p.prix, this.p.inflation, this.N],([$prix, $inflation, $N], _, update) => {update((tarif) => {if (!tarif || tarif.length < $N) {tarif = Array($N);}for (let i = 0; i < $N; i++) {tarif[i] = $prix * Math.pow(1 + $inflation / 100, i)}return tarif})})this.tarifAllo = derived([this.p.coutElec, this.p.inflationElec, this.N],([$coutElec, $inflationElec, $N], _, update) => {update((tarif) => {if (!tarif || tarif.length < $N) {tarif = Array($N);}for (let i = 0; i < $N; i++) {tarif[i] = $coutElec * Math.pow(1 + $inflationElec / 100, i)}return tarif})})this.chiffreAffaires = derived([this.primeInvestissement, this.productionAnnuelle,this.p.autoconso, this.tarifLocal, this.tarifSurplus,this.p.autoprod, this.tarifAllo, this.N],([$primeInvestissement, $productionAnnuelle, $autoconso, $tarifLocal, $tarifSurplus, $autoprod, $tarifAllo, $N], _, update) => {update((ca) => {if (!ca || ca.length < $N) {ca = Array($N);}for (let i = 0; i < $N; i++) {ca[i] = (i == 0 ? $primeInvestissement : 0)+ $productionAnnuelle[i] * ($autoconso / 100 * $tarifLocal[i] +(1 - $autoconso / 100) * $tarifSurplus[i] +($autoprod / 100 * $tarifAllo[i]))}return ca})})this.turpe = derived([this.p.puissance, this.p.turpe, this.p.autoconso, this.productionAnnuelle, this.p.turpeInjection, this.p.inflation, this.N],([$puissance, $turpe, $autoconso, $productionAnnuelle, $turpeInjection, $inflation, $N], _, update) => {update((turpe) => {if (!turpe || turpe.length < $N) {turpe = Array($N);}for (let i = 0; i < $N; i++) {turpe[i] = ($puissance * $turpe +(1 - $autoconso / 100) * $productionAnnuelle[i] * $turpeInjection) * Math.pow(1 + $inflation / 100, i)}return turpe})})this.assurance = derived([this.investissement, this.p.assurance, this.p.inflation, this.N],([$investissement, $assurance, $inflation, $N], _, update) => {update((assurance) => {if (!assurance || assurance.length < $N) {assurance = Array($N);}for (let i = 0; i < $N; i++) {assurance[i] = (($investissement * $assurance) / 100)* Math.pow(1 + $inflation / 100, i)}return assurance})})this.exploitation = derived([this.p.puissance, this.p.maintenance, this.p.inflation, this.N],([$puissance, $maintenance, $inflation, $N], _, update) => {update((expl) => {if (!expl || expl.length < $N) {expl = Array($N);}for (let i = 0; i < $N; i++) {expl[i] = $puissance * $maintenance* Math.pow(1 + $inflation / 100, i)}return expl})})this.divers = derived([this.p.divers, this.p.puissance, this.p.inflation, this.N],([$divers, $puissance, $inflation, $N], _, update) => {update((divers) => {if (!divers || divers.length < $N) {divers = Array($N);}for (let i = 0; i < $N; i++) {divers[i] = $divers * $puissance* Math.pow(1 + $inflation / 100, i)}return divers})})this.charges = derived([this.investissement, this.p.fraisDivers, this.exploitation, this.turpe, this.assurance, this.divers, this.N],([$investissement, $fraisDivers, $exploitation, $turpe, $assurance, $divers, $N], _, update) => {update((charges) => {if (!charges || charges.length < $N) {charges = Array($N);}for (let i = 0; i < $N; i++) {charges[i] = ($investissement && i == 0 ? $fraisDivers : 0) + $exploitation[i] + $turpe[i] + $assurance[i] + $divers[i]}return charges})})this.valeurAjoutee = derived([this.chiffreAffaires, this.charges, this.N],([$chiffreAffaires, $charges, $N], _, update) => {update((va) => {if (!va || va.length < $N) {va = Array($N);}for (let i = 0; i < $N; i++) {va[i] = $chiffreAffaires[i] - $charges[i]}return va})})this.amortissement = derived([this.p.dureeAmortissement, this.investissement, this.N],([$dureeAmortissement, $investissement, $N], _, update) => {update((a) => {if (!a || a.length < $N) {a = Array($N);}for (let i = 0; i < $N; i++) {if (i < $dureeAmortissement) {a[i] = $investissement / $dureeAmortissement} else {a[i] = 0}}return a})});this.provisionOnduleurs = derived([this.p.puissance, this.p.provisionOnduleurs, this.p.inflation, this.N],([$puissance, $provision, $inflation, $N], _, update) => {update((a) => {if (!a || a.length < $N) {a = Array($N);}for (let i = 0; i < $N; i++) {a[i] = $puissance * 1000 * $provision * Math.pow(1 + $inflation / 100, i) / 10}return a})});// Accise: taux dérogatoire d'1€/MWh jusqu'au 31/1/2025this.accise = derived([this.productionAnnuelle, this.N],([$productionAnnuelle, $N], _, update) => {update((a) => {if (!a || a.length < $N) {a = Array($N);}for (let i = 0; i < $N; i++) {a[i] = $productionAnnuelle[i] * 0.001}return a})});// Exonération de taxe foncière: https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000044996150this.taxeFonciere = readable(0)// Formule compliquée, exonéré sur les cas concretsthis.cfe = readable(0)this.ifer = derived([this.p.puissance, this.p.ratioDcAc, this.N],([$puissance, $ratio, $N], _, update) => {update((a) => {if (!a || a.length < $N) {a = Array($N);}for (let i = 0; i < $N; i++) {a[i] = $puissance < 100 ? 0 : 3.394 * $puissance * $ratio}return a})});this.impots = derived([this.ifer, this.taxeFonciere, this.cfe, this.N],([$ifer, $taxeFonciere, $cfe, $N], _, update) => {update((a) => {if (!a || a.length < $N) {a = Array($N);}for (let i = 0; i < $N; i++) {a[i] = $ifer[i] + $taxeFonciere + $cfe}return a})});this.ebe = derived([this.valeurAjoutee, this.impots, this.N],([$va, $impots, $N], _, update) => {update((a) => {if (!a || a.length < $N) {a = Array($N);}for (let i = 0; i < $N; i++) {a[i] = $va[i] - $impots[i]}return a})});this.dette = derived([this.investissement, this.p.apport],([$inv, $app]) => $inv - $app)this.dette.subscribe(this.banque.dette.set)this.interetsDetteSenior = derived([this.banque.resultat, this.N],([$banque, $N], _, update) => {update((ids) => {if (!ids || ids.length < $N) {ids = Array($N);}for (let i = 0; i < $N; i++) {ids[i] = i < $banque.interetsAnnuels.length ? $banque.interetsAnnuels[i] : 0}return ids})})this.interetsDSRA = derived([this.interetsDetteSenior, this.p.tauxDSRA, this.N],([$ids, $taux, $N], _, update) => {update((a) => {if (!a || a.length < $N) {a = Array($N);}for (let i = 0; i < $N; i++) {a[i] = $ids[i] * $taux / 100}return a})});this.resultatFiscal = derived([this.ebe, this.amortissement, this.provisionOnduleurs, this.interetsDetteSenior, this.interetsDSRA, this.N],([$ebe, $amortissement, $provision, $ids, $dsra, $N], _, update) => {update((rf) => {if (!rf || rf.length < $N) {rf = Array($N);}for (let i = 0; i < $N; i++) {rf[i] = $ebe[i] - $amortissement[i] - $provision[i] - $ids[i] - $dsra[i]}return rf})})this.is = derived([this.resultatFiscal, this.p.tauxIS, this.N],([$r, $taux, $N], _, update) => {update((is) => {if (!is || is.length < $N) {is = Array($N);}for (let i = 0; i < $N; i++) {is[i] = $r[i] <= 0 ? 0 : ($r[i] * $taux / 100)}return is})})this.resultatNet = derived([this.resultatFiscal, this.is, this.N],([$r, $is, $N], _, update) => {update((r) => {if (!r || r.length < $N) {r = Array($N);}for (let i = 0; i < $N; i++) {r[i] = $r[i] - $is[i]}return r})})this.tresorerieDisponibleDetteSenior = derived([this.ebe, this.is, this.N],([$ebe, $is, $N], _, update) => {update((r) => {if (!r || r.length < $N) {r = Array($N);}for (let i = 0; i < $N; i++) {r[i] = $ebe[i] - $is[i]}return r})})this.serviceDetteSenior = derived([this.interetsDSRA, this.interetsDetteSenior, this.banque.resultat, this.N],([$dsra, $ids, $b, $N], _, update) => {update((r) => {if (!r || r.length < $N) {r = Array($N);}for (let i = 0; i < $N; i++) {r[i] = $dsra[i] + $ids[i] + $b.remboursementAnnuel[i]}return r})})this.dscr = derived([this.banque.resultat, this.tresorerieDisponibleDetteSenior,this.serviceDetteSenior, this.N],([$b, $treso, $service, $N], _, update) => {update((r) => {if (!r || r.length < $N) {r = Array($N);}for (let i = 0; i < $N; i++) {if (i < $b.remboursementAnnuel.length) {r[i] = $treso[i] / $service[i]} else {r[i] = 0}}return r})})}investissement: Readable<number>;apport: Readable<number>;primeInvestissement: Readable<number>;N: Writable<number>;tarifSurplus: Readable<Array<number>>;productionAnnuelle: Readable<Array<number>>;tarifLocal: Readable<Array<number>>;tarifAllo: Readable<Array<number>>;chiffreAffaires: Readable<Array<number>>;turpe: Readable<Array<number>>;assurance: Readable<Array<number>>;exploitation: Readable<Array<number>>;divers: Readable<Array<number>>;charges: Readable<Array<number>>;valeurAjoutee: Readable<Array<number>>;accise: Readable<Array<number>>;taxeFonciere: Readable<number>;cfe: Readable<number>;ifer: Readable<Array<number>>;impots: Readable<Array<number>>;ebe: Readable<Array<number>>;amortissement: Readable<Array<number>>;resultatFiscal: Readable<Array<number>>;provisionOnduleurs: Readable<Array<number>>;interetsDetteSenior: Readable<Array<number>>;interetsDSRA: Readable<Array<number>>;is: Readable<Array<number>>;dette: Readable<number>;resultatNet: Readable<Array<number>>;dscr: Readable<Array<number>>;tresorerieDisponibleDetteSenior: Readable<Array<number>>;serviceDetteSenior: Readable<Array<number>>;banque: Banque = new Banque();}import { ppmt } from 'financial'export type Resultat = {remboursement: Array<number>,remboursementAnnuel: number[],crd: number[],crdAnnuel: number[],interets: number[],interetsAnnuels: number[]}export class Banque {// V3dette = writable(0);// V4taux = writable(0);// Z8duree = writable(0);// Z7dureeDifferee = writable(1)// V8nombrePeriodesParAn = writable(12)prorataPeriodique = derived(this.nombrePeriodesParAn, (n) => 1 / n)// V6dureeAns = derived([this.duree, this.nombrePeriodesParAn], ([$a, $b]) => $a / $b);dureeAns_ = derived(this.dureeAns, Math.ceil)taux_ = derived(this.taux, (t) => Math.min(t, 1))// V10nombreEcheancesPeriodeTronquee = derived([this.dureeAns, this.nombrePeriodesParAn], ([a, b]) => a * b);// V9nombreEcheances = derived(this.nombreEcheancesPeriodeTronquee, Math.ceil)// W11totalPeriodes = derived([this.dureeAns, this.nombrePeriodesParAn, this.dureeDifferee], ([$a, $b, $c]) => $a * $b - $c)// X11periodeRemboursementCapital = this.dureeDiffereeresultat: Readable<Resultat> = derived([this.duree, this.dureeAns_, this.dette,this.periodeRemboursementCapital,this.nombreEcheancesPeriodeTronquee,this.taux_,this.prorataPeriodique,this.periodeRemboursementCapital,this.totalPeriodes,this.nombreEcheances,this.nombrePeriodesParAn,], ([$duree, $dureeAns_, $dette, $periode,$nombreEcheancesPeriodeTronquee,$taux_,$prorataPeriodique,$periodeRemboursementCapital,$totalPeriodes,$nombreEcheances,$nombrePeriodesParAn,], _, update) => {update((resultat) => {if (resultat.remboursement.length != $duree + 1) {resultat.remboursement = new Array($duree + 1)resultat.crd = new Array($duree + 1)resultat.interets = new Array($duree + 1)resultat.remboursementAnnuel = new Array($dureeAns_)resultat.crdAnnuel = new Array($dureeAns_)resultat.interetsAnnuels = new Array($dureeAns_)}resultat.crd[0] = $detteresultat.remboursement[0] = 0resultat.interets[0] = 0resultat.crdAnnuel[0] = 0resultat.remboursementAnnuel[0] = 0resultat.interetsAnnuels[0] = 0for (let i = 1; i <= $duree; i++) {if (i <= $periode) {resultat.remboursement[i] = 0} else if (i < $nombreEcheancesPeriodeTronquee) {resultat.remboursement[i] = ppmt($taux_ * $prorataPeriodique,i - $periodeRemboursementCapital,$totalPeriodes,-$dette)} else if (i == $nombreEcheances) {resultat.remboursement[i] = resultat.crd[i - 1]} else {resultat.remboursement[i] = 0}resultat.crd[i] = resultat.crd[i - 1] - resultat.remboursement[i]resultat.interets[i] = resultat.crd[i - 1] * $taux_ * $prorataPeriodiqueif (i % $nombrePeriodesParAn == 0) {resultat.crdAnnuel[i / $nombrePeriodesParAn] = resultat.crd[i]resultat.remboursementAnnuel[i / $nombrePeriodesParAn] = 0resultat.interetsAnnuels[i / $nombrePeriodesParAn] = 0}resultat.remboursementAnnuel[Math.floor(i / $nombrePeriodesParAn)] +=resultat.remboursement[i]resultat.interetsAnnuels[Math.floor(i / $nombrePeriodesParAn)] += resultat.interets[i]}return resultat})}, {remboursement: new Array(0),remboursementAnnuel: new Array(0),crd: new Array(0),crdAnnuel: new Array(0),interets: new Array(0),interetsAnnuels: new Array(0),});}export type PVGIS = {inputs: {location: { latitude: number, longitude: number },mounting_system: {fixed: { slope: { value: number }, azimuth: { value: number } },},},outputs: {hourly: {time: string'G(i)': numberP: numberT2m: numberH_sun: numberInt: number}[],},}export async function updatePv(puissance: number,communaute: (Grain.Communaute & { isMoving?: boolean }) | null,pvgis: PVGIS | null,soleil: Sample,base: string,): Promise<PVGIS|null> {if (communaute?.isMoving) {return pvgis}console.log("updatePv", puissance);let azimuth = 0;let p_total = 0;let inclinaison = 0;let lat = Math.round(communaute?.lat || 45.56747);let lng = Math.round(communaute?.lng || 5.92285);console.log("com", communaute)if (communaute?.compteurs) {for (let c of communaute.compteurs) {azimuth += c.azimuth * c.puissance;inclinaison += c.inclinaison * c.puissance;p_total += c.puissance;}if (p_total <= puissance) {inclinaison += lat * (puissance - p_total)} else {azimuth *= puissance / p_total;inclinaison *= puissance / p_total;}if (puissance > 0) {azimuth /= puissanceinclinaison /= puissance} else {azimuth = 0;inclinaison = lat;}azimuth = Math.round(azimuth)inclinaison = Math.round(inclinaison)}let d = new Date()let min_t = new Date(d.getFullYear(), 0, 1).getTime() / 1000let max_t = min_t + 364 * 24 * 3600if (!pvgis ||pvgis.inputs.location.latitude != lat ||pvgis.inputs.location.longitude != lng ||pvgis.inputs.mounting_system.fixed.slope['value'] != inclinaison ||pvgis.inputs.mounting_system.fixed.azimuth['value'] != azimuth) {await tick()console.log(inclinaison, azimuth, p_total);let annuelle = await fetch(`${base}/pvgis?lat=${lat}&lng=${lng}&azimuth=${azimuth}&inclinaison=${inclinaison}`)pvgis = await annuelle.json()}console.log(pvgis);soleil.hourly = { x: [], y: [] };soleil.daily = { x: [], y: [] };soleil.weekly = { x: [], y: [] };const start = new Date(min_t * 1000)const janvier = new Date(start.getFullYear(), 0, 1)console.log("UP")for (let t = min_t; t < max_t; t += 3600) {let tt = new Date(t * 1000)let tts = tt.toLocaleString()let tds = tt.toLocaleDateString()let pp = 0if (pvgis) {let len = pvgis.outputs.hourly.lengthlet n = ((t * 1000 - janvier.getTime()) / 3600000) % lenconst pvvar = 'P'pp =(pvgis.outputs.hourly[Math.floor(n)][pvvar] * puissance) /1000soleil.hourly.x.push(tts)soleil.hourly.y.push(pp)if ((t - min_t) % (24 * 3600) == 0) {if ((t - min_t) % (24 * 7 * 3600) == 0) {soleil.weekly.x.push(tds)soleil.weekly.y.push(pp)} else {soleil.weekly.y[soleil.weekly.y.length - 1] += pp}soleil.daily.x.push(tds)soleil.daily.y.push(pp)} else {soleil.daily.y[soleil.daily.y.length - 1] += ppsoleil.weekly.y[soleil.weekly.y.length - 1] += pp}}}return pvgis}type Fetch = (input: string) => Promise<Response>;export async function profile_(assets: string, profils_: Record<string, { profils: string[] }>, name: string, _fetch?: Fetch): Promise<{ coefs: number[], total: number }> {await tick();console.log("assets", assets)let profiles = await Promise.all(profils_[name].profils.map(async (x) => {let url = `${assets}/profiles/${x}.csv`console.log("url", url)let resp = await (_fetch? _fetch:fetch)(url)return await resp.text()}))let annee = []let total = 0let file = 0for (let p of profiles) {let l = 0for (let line of p.split('\n')) {if (!line) {continue}const col = line.split(',')// const week = parseInt(col[0])// const dow = parseInt(col[1])const cs = parseFloat(col[2])const cj = parseFloat(col[3])for (let i = 0; i < 48; i++) {let ch = parseFloat(col[5 + i])const c = cs * cj * chif (file == 0) {annee.push(c)} else {annee[l] += c}total += c}l += 1}file += 1}return { coefs: annee, total }}export let profil_cache: Map<string, { coefs: number[]; total: number }> = new Map()export async function updatePlot(assets: string,annee: Record<number, number[]>,profils: Grain.Profil[],profils_: Record<string, { profils: string[] }>,somme: Sample,soleil: Sample,fetch?: Fetch,): Promise<{auto: number, totalProd: number, totalConso: number}> {let annee_: Record<string, { t: number[]; y: number[]; i: number }> = {}let d = new Date()const min_t = new Date(d.getFullYear(), 0, 1).getTime() / 1000const max_t = min_t + 364 * 24 * 3600for (let [prm, r] of Object.entries(annee)) {let d: { t: number[]; y: number[]; i: number } = {t: [],y: [],i: 0,}let i = 0for (let t = min_t; t < max_t; t += 1800) {d.t.push(t)d.y.push(r[i] / 1000)i += 1}annee_[prm] = d}if (profils.length > 0) {let pi = 0;for (let p of profils) {console.log("profile", p);let d: { t: number[]; y: number[]; i: number } = {t: [],y: [],i: 0,}let i = 0let pp = profil_cache.get(p.type)if (!pp) {profil_cache.set(p.type, await profile_(assets, profils_, p.type, fetch))}console.log("pp", pp);for (let t = min_t; t <= max_t; t += 1800) {d.t.push(t)if (pp) {d.y.push((pp.coefs[i] * p.conso) / pp.total)}i += 1}annee_[pi] = dpi += 1}}return sample(min_t, max_t, annee_, somme, soleil, {formatMatcher: "month, year",})}
<script lang="ts">import type { Banque } from '../calcul'import { getContext } from 'svelte'const b: Banque = getContext("banque")let annuel = trueconst banque = b.resultat</script><divclass="tab-pane fade active show p-3"id="banque-tab-pane"role="tabpanel"aria-labelledby="banque-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold">Tous <span class="text-primary">les recoins</span> de votreemprunt.</h3><div class="form-check form-switch"><inputclass="form-check-input"type="checkbox"role="switch"id="annuel"bind:checked={annuel} /><label class="form-check-label" for="annuel">Présentation annuelle</label></div>{#if annuel}<div class="my-3"><table class="table table-striped border-0 d-inline-block banque"><thead><tr><th>Année</th><th>Capital restant dû</th><th>Principal</th><th>Intérêts</th></tr></thead><tbody>{#each $banque.remboursementAnnuel as r, periode}<tr><td>{periode}</td><td>{$banque.crdAnnuel[periode].toFixed(0)}</td><td>{r.toFixed(0)}</td><td>{$banque.interetsAnnuels[periode].toFixed(0)}</td></tr>{/each}</tbody></table></div>{:else}<div class="my-3"><table class="table table-striped border-0 d-inline-block"><thead><tr><th>Mois</th><th>Capital restant dû</th><th>Principal</th><th>Intérêts</th></tr></thead><tbody>{#each $banque.remboursement as r, periode}<tr><td>{periode}</td><td>{$banque.crd[periode].toFixed(0)}</td><td>{r.toFixed(0)}</td><td>{#if periode > 0}{$banque.interets[periode].toFixed(0)}{/if}</td></tr>{/each}</tbody></table></div>{/if}</div><style lang="scss">table {font-size: 90%;}th {font-size: 1rem;font-weight: bold;}td {padding: 5px 5px;padding: 5px 7px 5px 10px;text-align: end;border: none;}</style>
import Banque from '../../Banque.svelte'import Simulation from './Simulation.svelte'import Description from './Description.svelte'import { page } from '$app/stores'import { assets as a } from '$app/paths'import { enhance, applyAction } from '$app/forms'import { onMount } from 'svelte'import type { SubmitFunction } from '@sveltejs/kit'import type { Grain } from '../../../helpers'
import type { Modal, Offcanvas } from 'bootstrap'import { email } from '../../../stores'export let prmsFeature: boolean = falseexport let assets: string = aexport let server = 'https://coturnix.fr'let zoom = 5type Prm = { prm: number | null; nom: string; prenom: string }export let data: {nom: stringporteur: stringadresse: stringtel: stringid: stringcentrales: Grain.Centrale[]img?: stringsimulation: Grain.SimulationCommunauteguest?: booleanprms: Prm[]email?: string}
import { Derived } from './calcul'import { getContext } from 'svelte'
$: p = data.simulationonMount(async () => {email.set(data.email || null)email.subscribe((value) => {if (value) data.email = value})
const tarifLocal = _simulation.tarifLocalconst chiffreAffaires = _simulation.chiffreAffairesconst N = _simulation.Nconst investissement = _simulation.investissementconst charges = _simulation.chargesconst interetsDetteSenior = _simulation.interetsDetteSeniorconst banque = _simulation.banque.resultatconst resultatNet = _simulation.resultatNetconst _coutElec = _simulation.p.coutElecconst inflationElec = _simulation.p.inflationElecconst autoprod = _simulation.p.autoprodconst productible = _simulation.p.productibleconst puissance = _simulation.p.puissanceconst degradation = _simulation.p.degradationconst _consoMoyenneFoyer = _simulation.p.consoMoyenneFoyer
for (let i of document.body.children) {if (i.id == 'grainSaveModal') {document.body.removeChild(i)}}let modal = document.getElementById('grainSaveModal')console.log('mount modal', modal)if (modal) {document.body.appendChild(modal)}if (!window.bootstrap) {console.log('no window bootstrap, load')window.bootstrap = await import('bootstrap')}menuElt = document.getElementById('menuoff')if (!menuElt) {return}menu = new window.bootstrap.Offcanvas(menuElt)const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')for (let t of popoverTriggerList) {new window.bootstrap.Popover(t)}})let saveModal_: null | Modal = nulllet menu: null | Offcanvas = nulllet menuElt: null | HTMLElement = nullasync function saveModal(_e: Event) {console.log('saveModal', _e)if (!saveModal_) {console.log('no saveModal')saveModal_ = new window.bootstrap.Modal('#grainSaveModal', {focus: false,keyboard: true,})}showSaveModalAfterMenuHide()menu?.hide()}function showSaveModalAfterMenuHide() {// menuElt?.removeEventListener('hidden.bs.offcanvas', showSaveModalAfterMenuHide)let c = document.getElementsByClassName('offcanvas-backdrop')for (const elt of c) {elt.parentElement?.removeChild(elt)}saveModal_?.show()}let save: SubmitFunction = async ({ formData }) => {console.log('save called')formData.set('data', JSON.stringify(p))formData.set('prms', JSON.stringify(data.prms))formData.set('centrales', JSON.stringify([centrale]))saveModal_?.hide()return async ({ result }) => {console.log(JSON.stringify(result))await applyAction(result)}}let expert = $page.url.hash == '#expert'$: compteActive = $page.url.hash == '#compte' ? 'active' : ''$: descriptionActive = $page.url.hash == '#description' ? 'active' : ''$: banqueActive = $page.url.hash == '#banque' ? 'active' : ''$: expertActive = expert && $page.url.hash == '#expert' ? 'active' : ''$: simuActive =(true || !data.guest) && $page.url.hash == '#simu' ? 'active' : ''$: syntheseActive =!compteActive &&!banqueActive &&!simuActive &&!expertActive &&!descriptionActive? 'active': ''$: compteShow = compteActive ? 'show' : ''$: banqueShow = banqueActive ? 'show' : ''$: descriptionShow = descriptionActive ? 'show' : ''$: simuShow = simuActive ? 'show' : ''$: syntheseShow = syntheseActive ? 'show' : ''$: expertShow = expertActive ? 'show' : ''let N = 30
let an = new Date().getFullYear()
let an = new Date().getFullYear()function tarifSurplus_(puissance: number): number {let resultat = p.tarifCRE.prix[0]for (let i = 0; i < p.tarifCRE.limites.length; i++) {if (puissance < p.tarifCRE.limites[i]) {return resultat} else {resultat = p.tarifCRE.prix[i]}}return resultat}$: investissement = p.puissance? p.puissance * 1000 * p.ratio + p.enedis: 0$: apport = Math.min(investissement, p.apport)$: primeInvestissement =(p.puissance <= 3 ? 0.30:p.puissance <= 9 ? 0.23:p.puissance <= 36 ? 0.20: p.puissance <= 100 ? 0.10: 0) * 1000 * p.puissance$: productionAnnuelle = (i: number) => H.productionAnnuelle(p, i)let surface_kwc = 4.5let surface = surface_kwc * (p?.puissance || 0)function onTauxChange(ev: Event) {let t = ev.target as HTMLInputElementconsole.log(ev, t.valueAsNumber)if (t.valueAsNumber > 100) {t.valueAsNumber = 100}}function onSurfaceChange(ev: Event) {surface = (ev.target as HTMLInputElement).valueAsNumberp.puissance = surface / surface_kwc}function onRatioChange(ev: Event) {surface_kwc = (ev.target as HTMLInputElement).valueAsNumbersurface = p.puissance * surface_kwc}function onPuissanceChange(ev: Event) {p.puissance = (ev.target as HTMLInputElement).valueAsNumbersurface = p.puissance * surface_kwc}$: autoconso = p.autoconso / 100$: tarifSurplus = (i: number) =>tarifSurplus_(p.puissance) * Math.pow(1 + p.tarifCRE.inflation / 100, i)$: tarifLocal = (i: number) => H.tarifLocal(p, i)$: tarifAllo = (i: number) =>p.coutElec * Math.pow(1 + p.inflationElec / 100, i)$: chiffreAffaires = (i: number) =>(i == 0 ? primeInvestissement : 0) +productionAnnuelle(i) *(autoconso * tarifLocal(i) +(1 - autoconso) * tarifSurplus(i) +(p.autoprod * tarifAllo(i)) / 100)$: exploitation = (i: number) =>p.puissance * p.maintenance * Math.pow(1 + p.inflation / 100, i)$: turpe = (i: number) =>(p.puissance * p.turpe +(1 - autoconso) * productionAnnuelle(i) * p.turpeInjection) *Math.pow(1 + p.inflation / 100, i)$: assurance = (i: number) =>((investissement * p.assurance) / 100) *Math.pow(1 + p.inflation / 100, i)$: divers = (i: number) =>p.divers * p.puissance * Math.pow(1 + p.inflation / 100, i)$: charges = (i: number) =>(investissement && i == 0 ? p.fraisDivers : 0) +exploitation(i) +turpe(i) +assurance(i) +divers(i)$: valeurAjoutee = (i: number) => chiffreAffaires(i) - charges(i)// Impots// Accise: taux dérogatoire d'1€/MWh jusqu'au 31/1/2025$: accise = (i: number) => productionAnnuelle(i) * 0.001$: ifer = (_: number) =>p.puissance < 100 ? 0 : 3.394 * p.puissance * p.ratioDcAc// Exonération de taxe foncière: https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000044996150$: taxeFonciere = (_: number) => 0// Formule compliquée, exonéré sur les cas concrets$: cfe = (_: number) => 0$: impots = (i: number) => ifer(i) + taxeFonciere(i) + cfe(i)// EBE$: ebe = (i: number) => valeurAjoutee(i) - impots(i)// Déductions fiscales$: amortissement = (i: number) =>i < p.dureeAmortissement ? investissement / p.dureeAmortissement : 0$: provisionOnduleurs = (i: number) =>(p.puissance *1000 *p.provisionOnduleurs *Math.pow(1 + p.inflation / 100, i)) /10let remboursementAnnuel: Array<number> = []let interetsAnnuels: Array<number> = []$: dette = Math.max(0, investissement - apport)$: interetsDetteSenior = (i: number) =>i < interetsAnnuels.length ? interetsAnnuels[i] : 0$: interetsDSRA = (i: number) => (interetsDetteSenior(i) * p.tauxDSRA) / 100$: tresorerieDisponibleDetteSenior = (i: number) => ebe(i) - is(i)$: serviceDetteSenior = (i: number) =>interetsDSRA(i) + interetsDetteSenior(i) + remboursementAnnuel[i]$: dscr = (i: number) =>i < remboursementAnnuel.length? tresorerieDisponibleDetteSenior(i) / serviceDetteSenior(i): 0$: resultatFiscal = (i: number) =>ebe(i) -amortissement(i) -provisionOnduleurs(i) -interetsDetteSenior(i) -interetsDSRA(i)$: is = (i: number) =>resultatFiscal(i) <= 0 ? 0 : (resultatFiscal(i) * p.tauxIS) / 100$: resultatNet = (i: number) => resultatFiscal(i) - is(i)
for (let i = 0; i < N; i++) {cout +=p.coutElec *Math.pow(1 + p.inflationElec / 100, i) *consoMoyenneFoyer(i)
for (let i = 0; i < $N; i++) {cout += $_coutElec * Math.pow(1 + $inflationElec / 100, i) * consoMoyenneFoyer(i)
(tarifLocal(i) * consoMoyenneFoyer(i) * p.autoprod) / 100 +(p.coutElec *Math.pow(1 + p.inflationElec / 100, i) *consoMoyenneFoyer(i) *(100 - p.autoprod)) /
($tarifLocal[i] * consoMoyenneFoyer(i) * $autoprod) / 100 +($_coutElec *Math.pow(1 + $inflationElec / 100, i) *consoMoyenneFoyer(i) *(100 - $autoprod)) /
let totalRembAnnuel_n = remboursementAnnuel.slice(0, n).reduce((a, b) => a + b, 0)let totalInteret_n = sum(interetsDetteSenior, n)let totalRembAnnuel = remboursementAnnuel.reduce((a, b) => a + b, 0)let totalInteret = sum(interetsDetteSenior, n)
let totalRembAnnuel_n = $banque.remboursementAnnuel.slice(0, n).reduce((a, b) => a + b, 0)let totalInteret_n = sum_($interetsDetteSenior, n)let totalRembAnnuel = $banque.remboursementAnnuel.reduce((a, b) => a + b, 0)let totalInteret = sum_($interetsDetteSenior, n)
<div class="container-lg p-3 pb-5 overflow-auto"><div class="d-flex flex-lg-row flex-column"><nav class="navbar navbar-expand-lg start-0 w-lg-25"><buttonclass="navbar-toggler"type="button"data-bs-toggle="offcanvas"data-bs-target="#menuoff"aria-label="Toggle navigation"><span class="navbar-toggler-icon" /></button><div class="d-lg-none ms-3 me-auto">{#if data.nom}<h1 class="my-0">{data.nom}</h1>{/if}</div></nav><divclass="offcanvas-lg offcanvas-start mb-auto offmenu"data-bs-scroll="true"tabindex="-1"id="menuoff"><div class="offcanvas-body off-padding-md"><div class="p-3 p-lg-0 overflow-auto">{#if data.nom}<h1>{data.nom}</h1>{/if}<div class="my-3 form-check form-switch"><inputclass="form-check-input"type="checkbox"role="switch"id="expertsw"bind:checked={expert} /><label class="form-check-label" for="expertsw">Niveau expert</label></div><h2 class="mt-5">Communauté</h2><div class="my-3"><label class="form-label" for="puissance">Puissance totale (kWc)</label><inputclass="form-control form-control-sm"type="number"id="puissance"min="1"max="3000"value={p.puissance}on:change={onPuissanceChange} />{#if p.puissance > 3000}<span class="text-warning"><iclass="bi bi-exclamation-circle-fill me-2" /> Unecommunauté d'autoconsommation ne peut dépasserplus de 3MWc. Les résultats obtenus seront doncpurement hypothétiques.</span>{/if}</div><div class="my-3"><label class="form-label" for="surface">Surface totale (m²)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Estimation de la surface de modules photovoltaïques en fonction de la puissance et du ratio (configurable en mode expert)."><i class="bi bi-question-circle" /></button></label><inputclass="form-control form-control-sm"type="number"id="surface"min="1"value={surface}on:change={onSurfaceChange} />
</script><div class="tab-content" id="syntheseTabContent"><divclass="tab-pane fade show active p-3"id="synthese-tab-pane"role="tabpanel"aria-labelledby="synthese-tab"tabindex="0"><h3 class="my-3 text-body fs-1 fw-bold">En un regard, <span class="text-primary">tout</span> votreprojet.</h3><div class="my-5 py-2 total"><h5>Synthèse durée totale {$N} ans</h5><div class="row px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format(sum_($chiffreAffaires, $N))}</div><div>Chiffre d'affaire</div>
<div class="mb-3"><label class="form-label" for="autoconso">Taux d'autoconsommation (%)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Pourcentage de la production consommée localement, à valider plus finement dans l'onglet Simulation. C'est la valeur que l'application Coturnix optimise."><i class="bi bi-question-circle" /></button></label><div class="d-flex"><inputclass="form-range me-4"type="range"id="autoconso"bind:value={p.autoconso} />{p.autoconso}%
<div class="mx-3 flex-column text-center"><div class="donnee">{format(sum_($charges, $N))}
<div class="mb-3"><label class="form-label" for="autoprod">Taux d'autoproduction (%)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Pourcentage des consommations venant de sources locales, à valider plus finement dans l'onglet Simulation."><i class="bi bi-question-circle" /></button></label><div class="d-flex"><inputclass="form-range me-4"type="range"id="autoprod"bind:value={p.autoprod} />{p.autoprod}%
<div class="flex-column text-center"><div class="donnee">{format(sum_($resultatNet, $N))}
</div><div class="mb-3"><label class="form-label" for="prix">Prix d'achat fournisseur (€/kWh)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Prix d'achat moyen des consommateurs de la communauté à leur fournisseur d'électricité habituel (par exemple EDF)."><i class="bi bi-question-circle" /></button></label><inputclass="form-control form-control-sm"type="number"step="0.01"min="0"id="coutElec"bind:value={p.coutElec} />
<div>Résultat net</div>
<div class="mb-3"><label class="form-label" for="prix">Prix de vente local (€/kWh)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Hypothèse sur le prix de vente moyen. Dans Coturnix, les producteurs et les consommateurs choisissent leurs prix acceptables, et Coturnix calcule à chaque instant le prix qui maximise la valeur totale créée par la communauté."><i class="bi bi-question-circle" /></button></label><inputclass="form-control form-control-sm"type="number"step="0.01"min="0"id="prix"bind:value={p.prix} /></div><div class="mb-3"><label class="form-label" for="echeances">Durée d'analyse (an)</label><inputclass="form-control form-control-sm"type="number"id="echeances"min="0"bind:value={N} /></div>
</div></div>
<h2 class="mt-5">Financement</h2><div class="my-3"><label class="form-label" for="apport">Apport initial (€)</label><inputclass="form-control form-control-sm"type="number"id="apport"min="0"step="1000"bind:value={p.apport} />
<div class="row px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format($investissement)}</div><div>Montant de l'investissement</div>
<div class="mb-3"><label class="form-label" for="interet">Taux d'intérêt(%)</label><inputclass="form-control form-control-sm"type="number"step="0.1"id="interet"min="0"max="100"on:change={onTauxChange}on:keyup={onTauxChange}bind:value={p.interet} />
<div class="mx-3 flex-column text-center"><div class="donnee">{rentabiliteTaux(sum_($resultatNet, $N))}</div><div>Retour sur investissement</div>
<div class="mb-3"><label class="form-label" for="echeances">Nombre d'échéances (en mois)</label><inputclass="form-control form-control-sm"type="number"id="echeances"min="0"bind:value={p.echeances} />
<div class="flex-column text-center"><div class="donnee">{roiDate()}</div><div>Point de rentabilité</div>
{#if data.id}<form id="delForm" method="POST" action="?/del" /><buttonclass="my-2 me-2 btn btn-primary"on:click={saveModal}>Sauvegarder</button><buttonclass="my-2 btn btn-outline-primary"form="delForm">Supprimer</button>{:else if data.email}<buttonclass="my-2 btn btn-primary"on:click={saveModal}>Sauvegarder</button>{/if}
<div class="ps-lg-5 w-100 ms-0 z0"><ul class="mt-5 mt-lg-3 nav nav-tabs" id="tabs" role="tablist"><li class="nav-item" role="presentation"><ahref="#synthese"class="nav-link {syntheseActive}"id="synthese-tab"type="button"role="tab"aria-controls="synthese-tab-pane"aria-selected="true">Synthèse</a></li><li class="nav-item" role="presentation"><ahref="#compte"class="nav-link {compteActive}"id="profile-tab"type="button"role="tab"aria-controls="compte-tab-pane"aria-selected="false">Détails financiers</a></li><li class="nav-item" role="presentation"><ahref="#description"class="nav-link {descriptionActive}"id="profile-tab"type="button"role="tab"aria-controls="description-tab-pane"aria-selected="false">Description</a></li><li class="nav-item" role="presentation"><ahref="#banque"class="nav-link {banqueActive}"id="banque-tab"type="button"role="tab"aria-controls="banque-tab-pane"aria-selected="false">Emprunt</a></li>{#if expert}<li class="nav-item" role="presentation"><ahref="#expert"class="nav-link {expertActive}"id="expert-tab"type="button"role="tab"aria-controls="expert-tab-pane"aria-selected="false">Mode expert</a></li>{/if}{#if true || !data.guest}<li class="nav-item" role="presentation"><ahref="#simu"class="nav-link {simuActive}"id="prm-tab"type="button"role="tab"aria-controls="prm-tab-pane"aria-selected="false">Simulation</a></li>{/if}</ul><div class="tab-content" id="syntheseTabContent"><divclass="tab-pane fade {syntheseActive} {syntheseShow} p-3"id="synthese-tab-pane"role="tabpanel"aria-labelledby="synthese-tab"tabindex="0"><h3 class="my-3 text-body fs-1 fw-bold">En un regard, <span class="text-primary">tout</span> votreprojet.</h3><div class="my-5 py-2 total"><h5>Synthèse durée totale {N} ans</h5><div class="row px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format(sum(chiffreAffaires, N))}</div><div>Chiffre d'affaire</div></div><div class="mx-3 flex-column text-center"><div class="donnee">{format(sum(charges, N))}</div><div>Charges d'exploitation</div></div><div class="flex-column text-center"><div class="donnee">{format(sum(resultatNet, N))}</div><div>Résultat net</div></div></div></div><div class="row px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format(investissement)}</div><div>Montant de l'investissement</div></div><div class="mx-3 flex-column text-center"><div class="donnee">{rentabiliteTaux(sum(resultatNet, N))}</div><div>Retour sur investissement</div></div><div class="flex-column text-center"><div class="donnee">{roiDate()}</div><div>Point de rentabilité</div></div></div></div><div class="row flex-row-reverse px-4"><div class="d-flex justify-content-around p-5"><divclass="d-flex flex-column justify-content-between text-center me-3"><div class="py-3 my-auto"><svgviewBox="{Math.floor(-N * 0.05)} {Math.floor(-maxa - margin)} {Math.ceil(N * 1.1)} {Math.ceil(maxa - mina + 2 * margin)}"xmlns="http://www.w3.org/2000/svg"width="250px"height="100px"preserveAspectRatio="none"><pathd={traceArray(resultatNet, N)}fill="transparent"stroke-width="2"vector-effect="non-scaling-stroke"stroke="#b71515" /></svg></div><div>Résultat net</div></div><divclass="d-flex flex-column justify-content-between text-center ms-3"><divstyle="width:250px"class="py-3 my-auto"><divclass="spinner-border ms-3 loading"class:d-none={!pvgis_loading} /><svgclass:d-none={pvgis_loading ||!maxProd}viewBox="-10 0 270 {maxProd}"preserveAspectRatio="none"xmlns="http://www.w3.org/2000/svg"width="250px"height="100px">{#each prodWeekly as p, i}<pathd="M {i *5}, {maxProd} L {i *5}, {maxProd - p}"fill="transparent"stroke-width="2"stroke="#ffbf00" />{/each}</svg><svgclass="donnee"class:d-none={pvgis_loading ||maxProd}xmlns="http://www.w3.org/2000/svg"width="32"height="32"fill="currentColor"viewBox="0 0 16 16"><pathd="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278" /></svg></div><div>Production annuelle</div></div></div>
<div class="row flex-row-reverse px-4"><div class="d-flex justify-content-around p-5"><divclass="d-flex flex-column justify-content-between text-center me-3"><div class="py-3 my-auto"><svgviewBox="{Math.floor(-$N * 0.05)} {Math.floor(-maxa - margin)} {Math.ceil($N * 1.1)} {Math.ceil(maxa - mina + 2 * margin)}"xmlns="http://www.w3.org/2000/svg"width="250px"height="100px"preserveAspectRatio="none"><pathd={traceArray($resultatNet, $N)}fill="transparent"stroke-width="2"vector-effect="non-scaling-stroke"stroke="#b71515" /></svg>
<div class="row flex-row-reverse px-4"><div class="d-flex justify-content-around p-5"><div class="me-3 flex-column text-center"><div class="donnee">{N? format(sum(resultatNet, N) / N): '0€'}</div><div>Bénéfice annuel moyen</div></div><div class="ms-3 flex-column text-center"><div class="donnee">{#if coutElec() - coutElecAuto() > 0}{format(coutElec() - coutElecAuto())}({economieFoyerMois} mois){:else}Aucune 😢{/if}</div><div>Economies consommateur</div></div></div>
<divclass="d-flex flex-column justify-content-between text-center ms-3"><divstyle="width:250px"class="py-3 my-auto"><divclass="spinner-border ms-3 loading"class:d-none={!pvgis_loading} /><svgclass:d-none={pvgis_loading ||!maxProd}viewBox="-10 0 270 {maxProd}"preserveAspectRatio="none"xmlns="http://www.w3.org/2000/svg"width="250px"height="100px">{#each prodWeekly as p, i}<pathd="M {i *5}, {maxProd} L {i *5}, {maxProd - p}"fill="transparent"stroke-width="2"stroke="#ffbf00" />{/each}</svg><svgclass="donnee"class:d-none={pvgis_loading ||maxProd}xmlns="http://www.w3.org/2000/svg"width="32"height="32"fill="currentColor"viewBox="0 0 16 16"><pathd="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278" /></svg>
<div class="my-5 py-2 dix"><h5>Synthèse 10 ans</h5><div class="row flex-row-reverse px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format(sum(chiffreAffaires, 10))}</div><div>Chiffre d'affaire</div></div><div class="mx-3 flex-column text-center"><div class="donnee">{format(sum(charges, 10))}</div><div>Coût total</div></div><div class="flex-column text-center"><div class="donnee">{format(sum(resultatNet, 10))}</div><div>Résultat net</div></div></div>
<div class="row flex-row-reverse px-4"><div class="d-flex justify-content-around p-5"><div class="me-3 flex-column text-center"><div class="donnee">{$N? format(sum_($resultatNet, $N) / $N): '0€'}
<div class="row flex-row-reverse px-4 dix"><div class="d-flex justify-content-around p-5"><divclass="flex-column justify-content-between text-center"><div class="donnee">{rembPret(10)}%</div><div>Prêt remboursé</div></div><divclass="flex-column justify-content-between text-center"><div class="donnee">{rentabiliteTaux(sum(resultatNet, 10))}</div><div>Rentabilité</div></div></div>
<div>Bénéfice annuel moyen</div></div><div class="ms-3 flex-column text-center"><div class="donnee">{#if coutElec() - coutElecAuto() > 0}{format(coutElec() - coutElecAuto())}({economieFoyerMois} mois){:else}Aucune 😢{/if}
<div class="my-5 py-2 cinq"><h5>Synthèse 5 ans</h5><div class="row flex-row-reverse px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format(sum(chiffreAffaires, 5))}</div><div>Chiffre d'affaire</div></div><div class="mx-3 flex-column text-center"><div class="donnee">{format(sum(charges, 5))}</div><div>Coût total</div></div><div class="flex-column text-center"><div class="donnee">{format(sum(resultatNet, 5))}</div><div>Résultat net</div></div></div>
<div class="my-5 py-2 dix"><h5>Synthèse 10 ans</h5><div class="row flex-row-reverse px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format(sum_($chiffreAffaires, 10))}
<div class="row flex-row-reverse px-4"><div class="d-flex justify-content-around p-5"><div class="flex-column text-center"><div class="donnee">{rembPret(5)}%</div><div>Prêt remboursé</div></div>
<div>Chiffre d'affaire</div></div>
<div class="flex-column text-center"><div class="donnee">{rentabiliteTaux(sum(resultatNet, 5))}</div><div>Rentabilité</div></div></div>
<div class="mx-3 flex-column text-center"><div class="donnee">{format(sum_($charges, 10))}
<div class="my-5 py-2 moyenne"><h5>Synthèse année moyenne</h5><div class="row flex-row flex-wrap px-4"><div class="d-flex justify-content-between py-5"><div class="flex-column text-center mx-3"><div class="donnee">{format(sum(resultatNet, N) / N)}</div><div>Bénéfices moyen</div></div><div class="flex-column text-center mx-3"><div class="donnee">{format(sum(chiffreAffaires, N) / N)}</div><div>Chiffre d'affaire</div></div><div class="flex-column text-center mx-3"><div class="donnee">{fmt2.format((investissement? (sum(resultatNet, N) * 100) /investissement -100: 0) / N)}%</div><div>Rentabilité annuelle</div></div><div class="flex-column text-center mx-3"><div class="donnee">{#if coutElec() - coutElecAuto() > 0}{fmt0.format(((coutElec() - coutElecAuto()) *100) /coutElec())}%{:else}Aucune 😢{/if}</div><div>Économies consommateur</div></div></div>
<div class="flex-column text-center"><div class="donnee">{format(sum_($resultatNet, 10))}
<divclass="tab-pane fade {compteActive} {compteShow} p-3"id="compte-tab-pane"role="tabpanel"aria-labelledby="compte-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold"><span class="text-primary">Projetez-vous</span> dans votreprojet.</h3>Durée d'analyse : {N} ans<h4 class="my-3 mt-5">Résultat</h4><div class="ms-3 row"><h5 class="mt-5">Résultat net</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum(resultatNet, N))}</span></div><div class="d-flex flex-wrap"><div class="d-flex flex-column my-3"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Résultat net</div></div>{#each Array(Math.ceil(N)) as _, i}<div class="d-flex flex-column my-3"><div class="tcellt">{an + i}</div><div class="tcellt">{resultatNet(i).toFixed(arr)}</div></div>{/each}</div>
<div class="row flex-row-reverse px-4 dix"><div class="d-flex justify-content-around p-5"><divclass="flex-column justify-content-between text-center"><div class="donnee">{rembPret(10)}%</div><div>Prêt remboursé</div>
<h4 class="my-3">Détail</h4><div class="ms-3"><h5 class="mt-5">Chiffre d'affaires</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum(chiffreAffaires, N))}</span></div><div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Autoproduction (€)</div><div class="titreligne tcellt">Vente locale (€)</div><div class="titreligne tcellt">Vente de surplus (€)</div><div class="titreligne tcellt">Prime</div><div class="titreligne tcellt">Chiffre d'affaires</div></div>{#each Array(Math.ceil(N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{(productionAnnuelle(i) *((p.autoprod * tarifAllo(i)) / 100)).toFixed(0)}</div><div class="tcellt">{(productionAnnuelle(i) *(autoconso * tarifLocal(i))).toFixed(0)}</div><div class="tcellt">{(productionAnnuelle(i) *((1 - autoconso) * tarifSurplus(i))).toFixed(0)}</div><div class="tcellt">{#if i == 0}{primeInvestissement}{:else} {/if}</div><div class="tcellt">{chiffreAffaires(i).toFixed(arr)}</div></div>{/each}</div>
<h5 class="my-3">Charges d'exploitation</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum(charges, N))}</span>
<divclass="flex-column justify-content-between text-center"><div class="donnee">{rentabiliteTaux(sum_($resultatNet, 10))}
<div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Maintenance (€)</div><div class="titreligne tcellt">TURPE (€)</div><div class="titreligne tcellt">Assurance (€)</div><div class="titreligne tcellt">Divers</div><div class="titreligne tcellt">Total charges</div></div>{#each Array(Math.ceil(N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{exploitation(i).toFixed(arr)}</div><div class="tcellt">{turpe(i).toFixed(arr)}</div><div class="tcellt">{assurance(i).toFixed(arr)}</div><div class="tcellt">{divers(i).toFixed(arr)}</div><div class="tcellt">{charges(i).toFixed(arr)}</div></div>{/each}</div>
<div>Rentabilité</div></div></div></div></div>
<h5 class="my-3">Excédent brut d'exploitation</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum(ebe, N))}</span>
<div class="my-5 py-2 cinq"><h5>Synthèse 5 ans</h5><div class="row flex-row-reverse px-4"><div class="d-flex justify-content-between p-5"><div class="flex-column text-center"><div class="donnee">{format(sum_($chiffreAffaires, 5))}
<div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Valeur ajoutée (€)</div><div class="titreligne tcellt">Accise (€)</div><div class="titreligne tcellt">IFER (€)</div><div class="titreligne tcellt">Taxe foncière</div><div class="titreligne tcellt">Cotisation foncière des entreprises</div><div class="titreligne tcellt">Excédent brut d'exploitation</div></div>{#each Array(Math.ceil(N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{valeurAjoutee(i).toFixed(arr)}</div><div class="tcellt">{accise(i).toFixed(arr)}</div><div class="tcellt">{ifer(i).toFixed(arr)}</div><div class="tcellt">{taxeFonciere(i).toFixed(arr)}</div><div class="tcellt">{cfe(i).toFixed(arr)}</div><div class="tcellt">{ebe(i).toFixed(arr)}</div></div>{/each}</div><h5 class="my-3">Résultat net</h5><div class="fw-bold fs-4">Cumulé : <span class="text-primary">{format(sum(resultatNet, N))}</span>
<div class="mx-3 flex-column text-center"><div class="donnee">{format(sum_($charges, 5))}
<div class="d-flex flex-wrap"><div class="d-flex flex-column my-4"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">Onduleurs<buttontype="button"class="btn btn-sm btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Provision pour le remplacement des onduleurs"><i class="bi bi-question-circle" /></button></div><div class="titreligne tcellt">Amortissement</div><div class="titreligne tcellt">Intérêts dette senior</div><div class="titreligne tcellt">Intérêts DSRA <buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Dans un emprunt, compte bancaire qui permettra de payer le service de la dette si jamais l'entité emprunteuse n'est plus en mesure de le faire."><i class="bi bi-question-circle" /></button></div><div class="titreligne tcellt">Résultat fiscal</div><div class="titreligne tcellt">Impôt sur les sociétés</div><div class="titreligne tcellt">Résultat net</div></div>{#each Array(Math.ceil(N)) as _, i}<div class="d-flex flex-column my-4"><div class="tcellt">{an + i}</div><div class="tcellt">{provisionOnduleurs(i).toFixed(arr)}</div><div class="tcellt">{amortissement(i).toFixed(arr)}</div><div class="tcellt">{interetsDetteSenior(i).toFixed(arr)}</div><div class="tcellt">{interetsDSRA(i).toFixed(arr)}</div><div class="tcellt">{resultatFiscal(i).toFixed(arr)}</div><div class="tcellt">{is(i).toFixed(arr)}</div><div class="tcellt">{resultatNet(i).toFixed(arr)}</div></div>{/each}
<div class="flex-column text-center"><div class="donnee">{format(sum_($resultatNet, 5))}
{#if investissement > apport}<h5 class="my-3">Couverture de la dette</h5><div class="d-flex flex-wrap"><div class="d-flex flex-column my-3"><div class="titreligne tcellt">Année</div><div class="titreligne tcellt">DSCR</div></div>{#each Array(Math.ceil(N)) as _, i}<div class="d-flex flex-column my-3"><div class="tcellt">{an + i}</div><div class="tcellt">{fmt2.format(dscr(i))}</div></div>{/each}</div>{/if}
<div>Résultat net</div>
<divclass="tab-pane fade {descriptionActive} {descriptionShow} p-3"id="description-tab-pane"role="tabpanel"aria-labelledby="description-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold">Quelques <span class="text-primary">détails</span> de plus.</h3>{#if descriptionActive}<Descriptionbind:zoombind:position={centrale.position}bind:azimuth={centrale.azimuth}bind:inclinaison={centrale.inclinaison} />{/if}</div><divclass="tab-pane fade {banqueActive} {banqueShow} p-3"id="banque-tab-pane"role="tabpanel"aria-labelledby="banque-tab"tabindex="0"><h3 class="my-3 fs-1 fw-bold">Tous <span class="text-primary">les recoins</span> de votreemprunt.</h3><Banque{dette}taux={p.interet / 100}duree={p.echeances}bind:interetsAnnuelsbind:remboursementAnnuel /></div>{#if expert}<divclass="tab-pane fade {expertActive} {expertShow} p-3"id="expert-tab-pane"role="tabpanel"aria-labelledby="expert-tab"tabindex="0"><div style="max-width:300px"><div class="mb-3"><label class="form-label" for="enedis">Coût de raccordement Enedis</label><inputclass="form-control form-control-sm"type="number"id="enedis"min="0"bind:value={p.enedis} /></div><div class="mb-3"><label class="form-label" for="productible">Productible annuel (kWh/kWc)</label><inputclass="form-control form-control-sm"type="number"id="productible"min="0"bind:value={p.productible} /></div>
<div class="row flex-row-reverse px-4"><div class="d-flex justify-content-around p-5"><div class="flex-column text-center"><div class="donnee">{rembPret(5)}%</div><div>Prêt remboursé</div></div>
<div class="mb-3"><label class="form-label" for="ratio">Ratio installation (€/W)</label><inputclass="form-control form-control-sm"type="number"id="ratio"min="0"bind:value={p.ratio} /></div><div class="mb-3"><label class="form-label" for="degradation">Taux de dégradation PV(%)</label><inputclass="form-control form-control-sm"type="number"id="degradation"min="0"bind:value={p.degradation} /></div><div class="mb-3"><label class="form-label" for="dsra">Taux d'intérêt DSRA (%)</label><inputclass="form-control form-control-sm"type="number"id="dsra"min="0"step="any"bind:value={p.tauxDSRA} /></div><div class="mb-3"><label class="form-label" for="inflation">Inflation tarif local</label><inputclass="form-control form-control-sm"type="number"id="inflation"min="0"bind:value={p.inflation} /></div><div class="mb-3"><label class="form-label" for="inflationElec">Inflation nationale de l'électricité (%)</label><inputclass="form-control form-control-sm"type="number"bind:value={p.inflationElec}min="0"id="inflationElec" /></div><div class="my-3"><label class="form-label" for="surfacekwc">Surface d'un kWc (m²)</label><inputclass="form-control form-control-sm"type="number"id="surfacekwc"step ="any"value={surface_kwc}on:change={onRatioChange} /></div>
<div class="flex-column text-center"><div class="donnee">{rentabiliteTaux(sum_($resultatNet, 5))}
{/if}</div><div class="tab-content" id="simuTabContent"><divclass="tab-pane fade {simuActive} {simuShow} show p-3"id="simu-tab-pane"role="tabpanel"aria-labelledby="simu-tab"tabindex="0"><h4 class="my-3">Simulation</h4><Simulationid={data.id}{server}{assets}{prmsFeature}position={centrale.position}azimuth={centrale.azimuth}inclinaison={centrale.inclinaison}bind:prms={data.prms}puissance={p.puissance}bind:prodWeeklybind:maxProdbind:pvgis_loading />
</div><div class="modal" id="grainSaveModal" tabindex="-1" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-body"><h5 class="modal-title pb-3" id="saveModalLabel">Sauvegarder</h5><formmethod="POST"id="saveForm"action="?/save"use:enhance={save}><div class="mb-3"><label class="form-label" for="nom">Nom du projet</label><inputclass="form-control"type="text"id="nom"name="nom"bind:value={data.nom}required />
<div class="my-5 py-2 moyenne"><h5>Synthèse année moyenne</h5><div class="row flex-row flex-wrap px-4"><div class="d-flex justify-content-between py-5"><div class="flex-column text-center mx-3"><div class="donnee">{format(sum_($resultatNet, $N) / $N)}
<div class="mb-3"><label class="form-label" for="porteur">Prénom et nom du porteur du projet</label><inputclass="form-control"type="text"id="porteur"name="porteur"bind:value={data.porteur} /></div><div class="mb-3"><label class="form-label" for="adresse">Adresse</label><inputclass="form-control"type="text"id="adresse"name="adresse"bind:value={data.adresse} />
<div>Bénéfices moyen</div></div><div class="flex-column text-center mx-3"><div class="donnee">{format(sum_($chiffreAffaires, $N) / $N)}
<div class="mb-3"><label class="form-label" for="tel">Téléphone</label><inputclass="form-control"type="text"id="tel"name="tel"bind:value={data.tel} />
<div>Chiffre d'affaire</div></div><div class="flex-column text-center mx-3"><div class="donnee">{fmt2.format(($investissement? (sum_($resultatNet, $N) * 100) /$investissement -100: 0) / $N)}%
<div class="mb-3"><label class="form-label" for="photo">Photo</label><inputclass="form-control"type="file"id="photo"name="photo" />
<div>Rentabilité annuelle</div></div><div class="flex-column text-center mx-3"><div class="donnee">{#if coutElec() - coutElecAuto() > 0}{fmt0.format(((coutElec() - coutElecAuto()) *100) /coutElec())}%{:else}Aucune 😢{/if}
<div class="modal-footer"><button form="saveForm" class="btn btn-primary">Sauvegarder</button><buttontype="button"class="btn btn-outline-primary"data-bs-dismiss="modal">Annuler</button></div>
@media (prefers-color-scheme: light) {nav {background-color: #fff !important;}}.offmenu {padding-top: 40px;}.z0 {z-index: 0;}
input.prix::-webkit-outer-spin-button,input.prix::-webkit-inner-spin-button {/* display: none; <- Crashes Chrome on hover */-webkit-appearance: none;margin: 0; /* <-- Apparently some margin are still there even though it's hidden */}input.prix[type='number'] {-moz-appearance: textfield; /* Firefox */}
import { error } from '@sveltejs/kit'import type { PageServerLoad, Actions } from './$types'import { userForm, Grain, save, del } from '../../../helpers'export const load: PageServerLoad = async ({ params, platform }) => {console.log('load', params['id'])if (!platform) {return null}if (params['id'] == 'nouveau') {const sim = Grain.newSimulationCommunaute()console.log(JSON.stringify(sim))return <Grain.GetProjetResp & { guest?: boolean }>{nom: '',id: '',porteur: '',tel: '',adresse: '',simulation: { ...sim },guest: false,email: 'grain@coturnix.fr',prms: [],centrales: [],}}const id_ = platform.env.USER.idFromName('grain')const projet = await userForm(platform.env, id_, {GetProjet: params['id'],})console.log('projet', JSON.stringify(projet))if (projet) {return <Grain.GetProjetResp>projet}
import type { Actions } from './$types'import { save, del } from '../../../helpers'
<script lang="ts">import { page } from '$app/stores'import type { Grain } from '../../../helpers'import { writable } from 'svelte/store'import { setContext, onMount } from 'svelte'import { Derived } from './calcul'import { email } from '../../../stores'export let data: {nom: stringporteur: stringadresse: stringtel: stringid: stringcommunaute?: Grain.Communaute&{isMoving?: boolean},img?: stringsimulation: Grain.SimulationCommunauteguest?: booleanprms: Grain.Prm[]profils: Grain.Profil[]email?: stringaccesParticuliers?: booleanexpert: booleanN: number};let simulation = new Derived(data.simulation);setContext("simulation", simulation)setContext("banque", simulation.banque)setContext("communaute", writable(null))let expert = writable(data.expert)setContext("expert", expert)export let root = "/grain/projet/"$: compteActive = $page.route.id?.endsWith("/finances") ?? false$: descriptionActive = $page.route.id?.endsWith("/description") ?? false$: banqueActive = $page.route.id?.endsWith("/banque") ?? false$: expertActive = $page.route.id?.endsWith("/expert") ?? false$: simuActive = $page.route.id?.endsWith("/simulation") ?? false$: syntheseActive = !compteActive && !descriptionActive && !banqueActive && !expertActive && !simuActivelet menuElt: null | HTMLElement = nullonMount(async () => {email.set(data.email || null)email.subscribe((value) => {if (value) data.email = value})if (!window.bootstrap) {console.log('no window bootstrap, load')window.bootstrap = await import('bootstrap')}menuElt = document.getElementById('menuoff')if (!menuElt) {console.log("no menu off")return}new window.bootstrap.Offcanvas(menuElt)const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')for (let t of popoverTriggerList) {new window.bootstrap.Popover(t)}})let surface_kwc = 4.5let surface = surface_kwc * data.simulation.puissancefunction onTauxChange(ev: Event) {let t = ev.target as HTMLInputElementconsole.log(ev, t.valueAsNumber)if (t.valueAsNumber > 100) {t.valueAsNumber = 100}}function onSurfaceChange(ev: Event) {if(simulation) {surface = (ev.target as HTMLInputElement).valueAsNumbersimulation.p.puissance.set(surface / surface_kwc)}}// function onRatioChange(ev: Event) {// if(simulation) {// surface_kwc = (ev.target as HTMLInputElement).valueAsNumber// surface = get(simulation.p.puissance) * surface_kwc// }// }function onPuissanceChange(ev: Event) {if(simulation) {simulation.p.puissance.set((ev.target as HTMLInputElement).valueAsNumber)surface = $puissance * surface_kwc}}const puissance = simulation?.p.puissanceconst autoconso = simulation?.p.autoconsoconst autoprod = simulation?.p.autoprodconst cout = simulation?.p.coutElecconst prix = simulation.p.prixconst apport = simulation.p.apportconst interet = simulation.p.interetconst echeances = simulation.p.echeancesconst N = simulation.N</script><div class="container-lg p-3 pb-5 overflow-auto"><div class="d-flex flex-lg-row flex-column"><nav class="navbar navbar-expand-lg start-0 w-lg-25"><buttonclass="navbar-toggler"type="button"data-bs-toggle="offcanvas"data-bs-target="#menuoff"aria-label="Toggle navigation"><span class="navbar-toggler-icon" /></button><div class="d-lg-none ms-3 me-auto"><h1>{data.nom}</h1></div></nav><divclass="offcanvas-lg offcanvas-start mb-auto offmenu"data-bs-scroll="true"tabindex="-1"id="menuoff"><div class="offcanvas-body off-padding-md"><div class="p-3 p-lg-0 overflow-auto"><input type="text" class="form-control input-h1" bind:value={data.nom} placeholder="Nouveau projet" /><form action="/projet/{data.id}/?save"><button class="my-2 btn btn-sm btn-primary">Sauvegarder</button></form>{#if data.email}<div class="my-3 form-check form-switch"><inputclass="form-check-input"type="checkbox"role="switch"id="expertsw"bind:checked={$expert} /><label class="form-check-label" for="expertsw">Niveau expert</label></div>{/if}<h2 class="mt-5">Communauté</h2><div class="my-3"><label class="form-label" for="puissance">Puissance totale (kWc)</label><inputclass="form-control form-control-sm"type="number"id="puissance"min="1"max="3000"value={$puissance}on:change={onPuissanceChange} />{#if data.simulation.puissance > 3000}<span class="text-warning"><iclass="bi bi-exclamation-circle-fill me-2" /> Unecommunauté d'autoconsommation ne peut dépasserplus de 3MWc. Les résultats obtenus seront doncpurement hypothétiques.</span>{/if}</div><div class="my-3"><label class="form-label" for="surface">Surface totale (m²)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Estimation de la surface de modules photovoltaïques en fonction de la puissance et du ratio (configurable en mode expert)."><i class="bi bi-question-circle" /></button></label><inputclass="form-control form-control-sm"type="number"id="surface"min="1"value={surface}on:change={onSurfaceChange} /></div><div class="mb-3"><label class="form-label" for="autoconso">Taux d'autoconsommation (%)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Pourcentage de la production consommée localement, à valider plus finement dans l'onglet Simulation. C'est la valeur que l'application Coturnix optimise."><i class="bi bi-question-circle" /></button></label><div class="d-flex"><inputclass="form-range me-4"type="range"id="autoconso"bind:value={$autoconso} />{$autoconso}%</div></div><div class="mb-3"><label class="form-label" for="autoprod">Taux d'autoproduction (%)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Pourcentage des consommations venant de sources locales, à valider plus finement dans l'onglet Simulation."><i class="bi bi-question-circle" /></button></label><div class="d-flex"><inputclass="form-range me-4"type="range"id="autoprod"bind:value={$autoprod} />{$autoprod}%</div></div><div class="mb-3"><label class="form-label" for="prix">Prix d'achat fournisseur (€/kWh)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Prix d'achat moyen des consommateurs de la communauté à leur fournisseur d'électricité habituel (par exemple EDF)."><i class="bi bi-question-circle" /></button></label><inputclass="form-control form-control-sm"type="number"step="0.01"min="0"id="coutElec"bind:value={$cout} /></div><div class="mb-3"><label class="form-label" for="prix">Prix de vente local (€/kWh)<buttontype="button"class="btn btn-link align-baseline p-0 ms-2"data-bs-toggle="popover"data-bs-trigger="hover focus"data-bs-content="Hypothèse sur le prix de vente moyen. Dans Coturnix, les producteurs et les consommateurs choisissent leurs prix acceptables, et Coturnix calcule à chaque instant le prix qui maximise la valeur totale créée par la communauté."><i class="bi bi-question-circle" /></button></label><inputclass="form-control form-control-sm"type="number"step="0.01"min="0"id="prix"bind:value={$prix} /></div><div class="mb-3"><label class="form-label" for="echeances">Durée d'analyse (an)</label><inputclass="form-control form-control-sm"type="number"id="echeances"min="0"bind:value={$N} /></div><h2 class="mt-5">Financement</h2><div class="my-3"><label class="form-label" for="apport">Apport initial (€)</label><inputclass="form-control form-control-sm"type="number"id="apport"min="0"step="1000"bind:value={$apport} /></div><div class="mb-3"><label class="form-label" for="interet">Taux d'intérêt(%)</label><inputclass="form-control form-control-sm"type="number"step="0.1"id="interet"min="0"max="100"on:change={onTauxChange}on:keyup={onTauxChange}bind:value={$interet} /></div><div class="mb-3"><label class="form-label" for="echeances">Nombre d'échéances (en mois)</label><inputclass="form-control form-control-sm"type="number"id="echeances"min="0"bind:value={$echeances} /></div>{#if data.id}<form method="POST" action="?/del"><button class="my-2 btn btn-outline-primary">Supprimer</button></form>{/if}</div></div></div><div class="ps-lg-5 w-100 ms-0 z0 main"><ul class="mt-5 mt-lg-3 nav nav-tabs" id="tabs" role="tablist"><li class="nav-item" role="presentation"><ahref="{root}{data.id}"class="nav-link {syntheseActive && "active"}"id="synthese-tab"type="button"role="tab"aria-controls="synthese-tab-pane"aria-selected="true">Synthèse</a></li><li class="nav-item" role="presentation"><ahref="{root}{data.id}/finances"class="nav-link {compteActive && "active"}"id="profile-tab"type="button"role="tab"aria-controls="compte-tab-pane"aria-selected="false">Détails financiers</a></li><li class="nav-item" role="presentation"><ahref="{root}{data.id}/description"class="nav-link {descriptionActive && "active"}"id="profile-tab"type="button"role="tab"aria-controls="description-tab-pane"aria-selected="false">Description</a></li><li class="nav-item" role="presentation"><ahref="{root}{data.id}/banque"class="nav-link {banqueActive && "active"}"id="banque-tab"type="button"role="tab"aria-controls="banque-tab-pane"aria-selected="false">Emprunt</a></li>{#if $expert}<li class="nav-item" role="presentation"><ahref="{root}{data.id}/expert"class="nav-link {expertActive && "active"}"id="expert-tab"type="button"role="tab"aria-controls="expert-tab-pane"aria-selected="false">Mode expert</a></li>{/if}{#if true || !data.guest}<li class="nav-item" role="presentation"><ahref="{root}{data.id}/simulation"class="nav-link {simuActive && "active"}"id="prm-tab"type="button"role="tab"aria-controls="prm-tab-pane"aria-selected="false">Simulation</a></li>{/if}</ul><slot/></div></div></div><style lang="scss">@media (prefers-color-scheme: light) {nav {background-color: #fff !important;}}@media (min-width: 992px) {.offmenu {position: fixed;top: 80px;height: calc(100% - 120px);padding-bottom: 20px;overflow: scroll;padding-right: 20px;width: 25%;max-width: 280px;}.main {margin-left: calc(min(25%, 280px)) !important;padding-left: 0;}}.input-h1 {font-size: 1.5em;font-weight: bold;}.z0 {z-index: 0;}.loading {color: #ffbf00;}.offcanvas-body h2 {font-size: 1.2em;}.titreligne {font-size: 1rem;font-weight: bold;}.donnee {font-size: 2rem;font-weight: bold;}.tcellt {border-bottom: 1px solid $primary;}.tcellt:last-child {border-bottom: unset;}.tcellt {padding: 8px 10px;}</style>
import { error } from '@sveltejs/kit'import type { LayoutServerLoad } from './$types'import { Grain } from '../../../helpers'import * as fs from 'node:fs'export const load: LayoutServerLoad = async ({ params }) => {if (params['id'] == 'nouveau') {const sim = Grain.newSimulationCommunaute()console.log(JSON.stringify(sim))return <Grain.GetProjetResp & { guest?: boolean }>{nom: '',id: 'nouveau',porteur: '',tel: '',adresse: '',simulation: { ...sim },guest: false,email: 'grain@coturnix.fr',prms: [],profils: [],N: 30,expert: false,}}try {return fs.readFileSync(params['id'], 'utf8');} catch (err) {console.error(err);return error(500, `${err}`)}}
export type SaveFunction = (e: Event, nom: string, simulation: SimulationCommunaute, prms: Prm[], profils: Profil[], communaute?: Communaute) => Promise<void>export type Rapport = {id: string,nom: string,simulation: SimulationCommunaute,communaute?: Communaute,production?: number[]charge?: Record<string, number[]>interets_annuel: number[],remboursement_annuel: number[],};export type RapportFunction = (r: Rapport) => Promise<void>
export type LatLng = { lat: number; lng: number }export type Derogation = 'non' | '10' | '20'export type Compteur = {lat: numberlng: numberpuissance: numberazimuth: numberinclinaison: number}export type Communaute = {lat: numberlng: numberderogation: Derogationcompteurs?: Compteur[]}export function profils(expert: boolean) {let profils_: Record<string, { profils: string[] }> = {'Résidentiel ≤6kVA': {profils: ['RES1-P1'],},'Résidentiel 6>kVA≤36': {profils: ['RES11-P1'],},'Résidentiel ≤36kVA HPHC': {profils: ['RES2'],},'Professionel ≤36kVA': {profils: ['PRO1-P1'],},'Professionnel ≤36kVA HWE': {profils: ['PRO1WE'],},'Professionnel ≤36kVA HPHC': {profils: ['PRO2'],},'Entreprise 36<kVA<250 HPHC ETE-HIV': {profils: ['ENT1'],},/*"Production hydraulique":{"profils":[ "PRD1-P1" ]},"Production cogénération":{"profils":[ "PRD2-P1" ]},"Production photovoltaïque":{"profils":[ "PRD3-P1" ]},*/}if(expert) {for (let p of ["ENT1-P1","ENT1-P2","ENT1-P3","ENT1-P4","ENT2-P1","ENT2-P2","ENT2-P3","ENT2-P4","ENT3-P1","ENT3-P2","ENT3-P3","ENT3-P4","ENT3-P5","ENT4-P1","ENT4-P2","ENT4-P3","ENT4-P4","ENT5-P1","ENT5-P2","ENT5-P3","ENT5-P4","ENT5-P5","ENT5-P6","ENT5-P7","ENT5-P8","ENT6-P1","ENT6-P2","ENT6-P3","ENT6-P4","ENT6-P5","ENT6-P6","ENT7-P1","ENT7-P2","ENT7-P3","ENT7-P4","ENT7-P5","PRD1-P1","PRD2-P1","PRD3-P1","PRD4-P1","PRO1-P1","PRO1WE-P1","PRO1WE-P2","PRO22WE-P1","PRO22WE-P2","PRO22WE-P3","PRO22WE-P4","PRO2-P1","PRO2-P2","PRO3-P1","PRO3-P2","PRO3-P3","PRO3-P4","PRO3-P5","PRO3-P6","PRO4-P1","PRO4-P2","PRO5-P1","PRO6-P1","PRO6-P2","PRO6-P3","PRO6-P4","RES11-P1","RES11WE-P1","RES11WE-P2","RES1-P1","RES1WE-P1","RES1WE-P2","RES22WE-P1","RES22WE-P2","RES22WE-P3","RES22WE-P4","RES2-P1","RES2-P2","RES2WE-P1","RES2WE-P2","RES2WE-P3","RES3-P1","RES3-P2","RES3-P3","RES3-P4","RES3-P5","RES3-P6","RES4-P1","RES4-P2","RES5-P1","RES5-P2","RES5-P3","RES5-P4",]) {profils_[p] = {profils: [p]}}}return profils_;}
export function productionAnnuelle(p: Grain.SimulationCommunaute, i: number) {return p.productible * p.puissance * Math.pow(1 - p.degradation / 100, i)
export function productionAnnuelle(productible: number, puissance: number, degradation: number, i: number) {return productible * puissance * Math.pow(1 - degradation / 100, i)