Ascendant
Developer guides

Divisional charts

Generate D9 and the other divisional charts, read their whole-sign houses, and map a single longitude into a division.

A divisional chart reworks the same planetary positions through a division of each sign. The D9, for example, splits every sign into nine parts. Ascendant derives all of them from the shared Placements, so asking for more divisions costs very little extra and never changes the D1.

This guide shows how to request divisions, read the result, tell divisional charts apart from D1 in your types, and map one longitude on its own.

Supported divisions

Ascendant supports D1 plus fifteen divisional charts:

DivisionCommonly known asDivisionCommonly known as
D2HoraD20Vimsamsa
D3DrekkanaD24Chaturvimsamsa
D4ChaturthamsaD27Bhamsa
D7SaptamsaD30Trimsamsa
D9NavamsaD40Khavedamsa
D10DasamsaD45Akshavedamsa
D12DwadasamsaD60Shashtiamsa
D16Shodasamsa

The type Chart.Division lists exactly these numbers, so TypeScript rejects any other value at compile time.

Request divisions

Pass the divisions you want as the second argument to Chart.generate:

import { Chart, Runtime } from "astro-ascendant";
import { DateTime, ManagedRuntime } from "effect";

const runtime = ManagedRuntime.make(Runtime.MainLayer);

const located = Chart.LocatedMoment.make({
  moment: Chart.Moment.make({ date: DateTime.makeUnsafe("2000-01-01T12:00:00Z") }),
  latitude: 12.9716,
  longitude: 77.5946,
});

const calculation = await runtime.runPromise(Chart.generate(located, [9, 10]));

The result's charts always starts with D1, followed by the divisions you asked for. They come back in numeric order with duplicates removed, so [10, 9, 9] gives D1, D9, and D10.

To get every divisional chart at once, list them all:

const everything = [2, 3, 4, 7, 9, 10, 12, 16, 20, 24, 27, 30, 40, 45, 60] as const;
const calculation = await runtime.runPromise(Chart.generate(located, everything));

D1 versus a divisional chart

calculation.charts is typed as a D1 chart followed by any number of divisional charts, so you can split it directly:

const [d1, ...divisional] = calculation.charts;

d1.angles; // chart angles, only on D1
d1.rulingPlanets; // only on D1
d1.houses[1].subLord; // KP lords, only on D1

When you have a Chart.Chart, a value that could be either, check its division to narrow it:

const describe = (chart: Chart.Chart) =>
  chart.division === 1 ? `D1 with ${chart.rulingPlanets.length} ruling planets` : `D${chart.division}`;

The two kinds differ like this:

D1 chartDivisional chart
Houses start atThe house system's cuspsThe start of each sign, counted from the mapped Lagna
Uses the house systemYesNo
Angles, KP lords, significations, ruling planetsYesNo
Houses per chart1212

Divisional charts have no KP data because those belong to source longitudes, and a mapped longitude is not a source longitude. Their houses are whole signs whatever house system you chose.

Read a divisional chart

Each chart has twelve houses, keyed 1 to 12. A divisional house has these fields:

FieldMeaning
signThe sign of the house.
cuspWhere the house begins, which is the start of its sign (for example 240 for Sagittarius).
signLordThe lord of that sign.
significationsThe house's classical significations.
planetsThe planets in the house, with their mapped degree and sign.
lagnaThe mapped Lagna, in the house that holds it. Otherwise null.

A chart also records its division and its provenance, which for every divisional chart is the Parashari whole-sign-from-mapped-lagna method.

To find where a planet sits, scan the houses. The data is one-directional on purpose: a house lists its planets, but a planet does not carry its house.

const signOf = (chart: Chart.Chart, planet: Chart.Planets) =>
  Object.values(chart.houses).find((house) =>
    house.planets.some((placed) => placed.name === planet),
  )?.sign;

const d9 = calculation.charts.find((chart) => chart.division === 9);
const sunInD9 = d9 && signOf(d9, "Sun"); // "Leo" for the birth above

Example: one birth through every division

For the birth above, the Lagna and Sun land in these signs:

DivisionLagnaSun
D1GeminiSagittarius
D2LeoCancer
D3GeminiAries
D4VirgoGemini
D7LeoPisces
D9SagittariusLeo
D10VirgoTaurus
D12VirgoGemini
D16TaurusLeo
D20AquariusCancer
D24PiscesVirgo
D27GeminiGemini
D30AquariusSagittarius
D40AriesAquarius
D45AquariusSagittarius
D60CapricornVirgo

The D1 uses the default KP settings, Placidus houses, but the sign of each planet is the same under any house system. The house system changes only D1's cusps.

Map one longitude

If you need only the divisional position of a single longitude, skip the chart and use getDivisionalTarget:

import { getDivisionalTarget } from "astro-ascendant/chart";
import { Effect } from "effect";

const target = await runtime.runPromise(getDivisionalTarget(256.5924, 9));
// {
//   signIndex: 4,          // 0 = Aries, so 4 = Leo
//   degree: 29.33,         // degree within the target sign
//   longitude: 149.33,     // the mapped longitude
// }

For the Sun at 256.59° in D1, the D9 target is Leo at 29.33°, which matches the Sun's D9 sign in the table above. For division 1, the function returns the source position unchanged. The same function is also exported from astro-ascendant/chart/divisional-mapping.

Where divisions are used

When it fails

  • An unsupported division number is a compile-time error. If a value gets past the types, for example through a cast, Chart.generate ends in a defect, not a typed failure, so validate numbers from untrusted input before you pass them in.
  • Chart.generate fails with a ChartCalculationError at the "houses" stage if the chosen house system's D1 cusps do not divide the circle into twelve houses.
  • getDivisionalTarget fails with a DivisionalMappingError when the longitude cannot be used.

See Errors and typed failures for how to handle them.

On this page