# Model

Which of the four models a profile solves with (or Custom, a list of option choices) and what the stored option choices of a Custom or Spin profile are.

## Generated table

**Record:** profile · **Covers:** `model`

| Field | Type | Required | Limits in the schema | Solver reads | In a shared log |
|---|---|---|---|---|---|
| `model.options` | object (the option choices) | no | - | yes | as stored |
| `model.preset` | Standard or Physics or Custom or Classic or Spin | yes, within | - | yes | as stored |

## What it holds

These two fields say how the solver is set up for this profile. `model.preset` picks one of five: Standard, Physics, Classic, Spin or Custom. `model.options` holds option choices, which only a Custom or a Spin profile uses. A profile with no `model` at all is a Standard profile. Nothing here depends on the bullet, the rifle or a place: it is the same setup for every shot of the profile. The calibration that a fit stores records the model it was fitted under in a field of its own, described on the [calibration](calibration.md) page.

## What to know first

**No model means Standard.** A missing `model` is solved as Standard. A preset the app does not know (one from a newer app) is solved as Standard by the app, with no message; the connector refuses it (since 2026-10-15).

**Stored options count only under Custom, and a few under Spin.** Standard, Physics and Classic ignore any stored option. Spin reads six keys (Magnus, estimator rules, yaw of repose, subsonic lift) and ignores the rest. Custom starts from Standard and applies every key it knows.

**The connector can choose a model but cannot make a Custom one.** Its `model` input is Standard, Physics, Classic or Spin, and no tool sets an option. Choosing a model through it drops the options stored before, those of a Custom or Spin profile too, without a question.

**Changing the model keeps the calibration and does not re-fit it.** No keep-or-reset question is asked. The old fit stays on the profile and is flagged "calibrated under" the old model until it is fitted again.

**A value the app does not know stops the solve, where it is read.** Under Custom, and for the six Spin keys under Spin, an unrecognised stored value is refused by name. An unknown preset differs: the app solves it as Standard; the connector refuses it.

## The fields

**`model.preset`** The model the profile solves with: Standard, Physics, Classic, Spin or Custom. Required when `model` is present. Absent `model` means Standard, and the profile screen's Model section writes one whenever a save changes something there, also for Standard. Standard follows the conventions most field calculators use and is the default. Physics uses the more exact models where the app has them. Classic is a second set of calculator conventions, with no Coriolis. Spin is a modified point-mass model: it follows the bullet's spin and the small yaw it settles into. A bullet entered as shorter than 2 calibers is solved as a plain point mass, and the answer says so. (A profile with no bullet length cannot be solved under any model.) Custom is Standard with the stored option choices applied on top. The solver's own list of what each model chooses, option by option, is the [options index](../options.md).

**`model.options`** The stored option choices, one key per option, named as in the options index. Optional, and an absent key means the model's own choice. Example of a hand-made Custom list: `soundSpeed` set to `moist` and `coriolis` set to `integrated`, nothing else (the Model section writes every key of its list). Which keys count depends on the preset.

- Standard, Physics and Classic read nothing from it. Stored keys are ignored.
- Spin reads `magnus`, `magnus_cnpa`, `magnus_slope`, `estimatorRules`, `yawRepose` and `subsonicLift`. Everything else is ignored.
- Custom reads every key of the 21 options, plus the two Magnus numbers. A key the app does not know is ignored.

Four options have the same value in all four models and are not in the Spin block, so only a Custom list can change them: `curvature`, `flight`, the Spin crosswind-jump choice (`aeroJumpSpin`) and the zero offset choice (the last is used by [zero](zero.md)). The two Magnus numbers are `model.options.magnus_cnpa` (the Magnus slope, flat in Mach) and `model.options.magnus_slope` (its change per Mach above Mach 2). The solver uses them only when the Magnus choice, `magnus`, is its own custom value, and the Model section stores them only then. Both are limited: -2 to 2 for `model.options.magnus_cnpa` and -1 to 1 for `model.options.magnus_slope`. The Model section's form, the record check on the server and the backup importer hold these limits, and the solver refuses a value outside them when the Magnus choice is custom. The connector cannot set either, so there is no table of limits by writer. A few option values are stored under older spellings than the option pages use; the app and the connector translate them in both directions, and you never type one.

## Who writes it

**The app.** In the profile screen's Model section, the Model list offers Standard, Physics, Classic, Custom and Spin, and every save writes the choice, also for Standard. A Custom save writes all the options in its list: every option except the four that belong to the Spin block (Magnus, estimator rules, yaw of repose, lift below the speed of sound). That list is filled once, when the profile is opened, from the options the profile resolves to now. So choosing Custom on a Physics profile starts the list at Physics's choices, not Standard's. The four Spin-block choices, and the Magnus numbers, are set in the Model section's Spin block for a Spin profile and for a Custom profile whose bullet flight is not point mass. They are saved only when they differ from their default, so a default is an absent key. Choosing Standard, Physics or Classic saves no options, so a Custom list is lost. Guided setup does not ask for the model, and a profile started in the app has none.

**The connector.** `edit_profile` and `create_profile` take `model`, with the four named models and not Custom. Setting it replaces the whole `model` with just the new preset, so earlier options are dropped. Giving the model the profile already has changes nothing, and an edit that changes nothing saves nothing. `create_profile` leaves `model` out for Standard. `edit_profile` to Standard stores a preset, which reads the same as none. Neither tool has an input for an option, and an input it does not know is not used, and the reply names it ("I ignored: ..."). A `model` input on `solve`, `range_card` and `sensitivity` picks a model for one answer and saves nothing; it cannot name Custom either. `compare_models` takes no model: it always answers with all four.

**Copies and restores.** Copying a shared profile (in the app or with `copy_shared_profile`) writes the model the sharer's record held (its ids in the current spelling, whichever the record held), and restoring a deleted profile writes it as the deleted version held it. The [origin](origin.md) and [versions](versions.md) pages describe the two.

**Never fitted.** No calibration writes these fields; a fit carries them to the new version unchanged, and records the model it used inside the calibration. The server does not set them.

## Who reads it

**The connector's check.** Since 2026-10-15 the connector's `solve`, `range_card`, `sensitivity`, `compare_models`, `calibrate_profile` and `next_target` refuse a profile whose preset is not one the connector knows, with "This profile's model isn't one I know", before any solve (connector fixes spec §10.2). No preset at all is still Standard.

**The solver.** One function turns the stored model into the options of every solve. It takes the frozen setup of the preset for Standard, Physics and Classic. For Spin it takes the Spin setup and puts the six Spin keys on top, when present. For Custom it takes Standard, then lets each known option key of the list replace its value. An unknown preset is treated as Standard. Before any flight, the solver checks every option value against its allowed list and refuses a wrong one by name.

Two stored options decide whether other stored fields count. The Magnus choice, [magnus](../options/magnus.md), decides whether the two Magnus numbers are used: only its custom value reads them. The bullet-flight choice, [dynamics](../options/dynamics.md), decides whether the Spin-block choices have any effect (the Model section offers and saves them only when the flight is not point mass). Every other option is explained on its own page, listed in the options index.

**The fitter.** A fit solves every group with the profile's own model, as the Calibrate screen does. The model is not a reason to leave a group out: groups logged under an earlier model are still used, all solved with the current one. After a fit, the calibration records the preset and options it used. The check "calibrated under" compares that record with the profile's model now, by the options each resolves to; a Standard, Physics or Classic profile differs only by preset, Custom and Spin also by their options, and a Spin fit made under older coefficients is flagged too.

**The app.** Every answer the app works out uses the profile's model: the Calculator, range cards, the Calibrate screen, the Workbench. The Calculator names the model and shows a second answer beside it: Standard when the model is Physics, Classic or Spin, Physics otherwise (Custom included). The editor and the Calculator's info line show the "calibrated under" note. The numbers stored with a logged group, the solver's prediction, are always Standard and Physics, whatever the profile's model; the [prediction](../log/prediction.md) page has them.

**The connector.** The tools that solve read the model, with one exception: `compare_models` runs the Standard, Physics and Classic models whatever the profile stores. `explain_dope` works in the profile's own model, with Coriolis off. `get_profile` shows the Custom list in the solver's value names. `calibrate_profile` fits under it and warns when the newest calibration record was made under another (a tall-target or velocity-against-temperature save writes a record of its own, so after one the warning is silent). `check_profile` uses it to say when a stored zero height or offset is ignored, and `explain_dope` to decide whether it counts. `compare_models` shows Spin as the profile's own setup only when the profile is a Spin profile, and as plain Spin otherwise. A `model` of Spin on a solve for a profile that is not Spin also gives plain Spin, not the profile's Custom choices.

## Why it is stored

**The preset.** Different calculators round the world differently, and the app is meant to match some and beat others. A profile keeps the model it was zeroed and shot with, so the same rifle can be tried under each. It is per profile and not per shot because a fit, a log and a come-up card only make sense under one.

**The options.** A Custom list lets one person turn on a single better choice, one option at a time, without taking a whole model. The Spin keys store the few choices the Model section's Spin block offers. Each is optional and an absent key is the default, so older profiles keep reading the same.

## Changing it

The edit category is none. A model change is not "shape", "ammo", "zero" or "scope factor", so the default is to keep the calibration, and the app asks no question. The connector saves a model change without asking too and reports the calibration as kept (or as none, if the profile had none). Called with `calibration` set to reset, it does reset. Both leave the old fit exactly as it was, so a fit made under another model is still applied to the new one until it is fitted again; the app and the connector say "calibrated under" the old model for that reason, and the Calibrate screen asks to run it again.

**Older groups.** The model does not decide which groups a fit may use (that is the job of the bullet, twist and its direction, sight height, zero range, zero air and zero slope). So after a model change a new fit uses the same groups as before, all under the new model. A group keeps the version it was shot with, and the [versions](versions.md) page explains that once for every field.

## Sharing and privacy

Both rows read "as stored": a reader of a shared log sees the model and the option list exactly as the owner does, on a share with one person, with the public or with the admins, and whether or not location and notes are hidden. A Custom list shows which conventions the owner chose, and a Spin profile shows which Magnus and estimator choices it was fitted with. Copying a shared profile brings the model with it.

---

[All reference pages](../index.md)
